/* processor.v - implementation of the 9086 bus interface unit. The logic that controls all external bus functions This file is part of the 9086 project. Copyright (c) 2023 Efthymios Kritikos This program 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. This program 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 this program. If not, see . */ //IOMEM: 1=IO 0=MEM `define BIU_HALT 4'b0000 `define BIU_NEXT_ACTION 4'b0001 `define BIU_READ 4'b0010 `define BIU_RESET 4'b0011 `define BIU_PUT_BYTE 4'b0100 `define BIU_PUT_UNALIGNED_16BIT_DATA 4'b0101 `define BIU_PUT_ALIGNED_16BIT_DATA 4'b0110 `define BIU_PUT_UNALIGNED_PREP_NEXT 4'b0111 `define BIU_PUT_UNALIGNED_PREP_NEXT2 4'b1000 `define BIU_WRITE_EXIT 4'b1001 `define BIU_WRITE_RELEASE 4'b1010 `define BIU_GET_ALIGNED_DATA 4'b1011 `define BIU_GET_UNALIGNED_DATA 4'b1100 `define BIU_GET_SECOND_BYTE 4'b1101 `define BIU_GET_SECOND_BYTE1 4'b1110 module BIU ( /*outside world*/ input clock, input reset, output reg [19:0] external_address_bus, /* */ inout [15:0] external_data_bus,output reg read, output reg write,output reg BHE,output reg IOMEM, /* internal */ output reg [31:0] INSTRUCTION, output reg VALID_INSTRUCTION, output reg [15:0] INSTRUCTION_LOCATION, input [1:0] NEXT_POSITION, /* */ input[15:0] ADDRESS_INPUT, inout [15:0] DATA, input write_request, input read_request, input Wbit, output reg VALID_DATA, input MEM_OR_IO ); reg [15:0] data_bus_output_register; assign external_data_bus=read?data_bus_output_register:16'hz; reg [15:0] DATA_OUT; reg DATA_DIR; assign DATA=DATA_DIR ? 16'hz:DATA_OUT; reg [7:0] INPUT_FIFO [15:0]; //8bit fifo memory with 4bit address bus reg [3:0] FIFO_start; /*inclusive*/ reg [3:0] FIFO_end; /*exclusive*/ reg [3:0] biu_state; always @(negedge reset) begin biu_state <= `BIU_HALT; end always @(posedge reset) begin biu_state <= `BIU_RESET; end reg jump_req; reg func; reg [19:0]INSTRUCTION_ADDRESS; reg [19:0]DATA_ADDRESS; assign external_address_bus= func? INSTRUCTION_ADDRESS : DATA_ADDRESS ; /* Read into the FIFO */ always @(posedge clock) begin if ( jump_req ) begin /* verilator lint_off BLKSEQ */ FIFO_start = 4'b0; /* verilator lint_on BLKSEQ */ FIFO_end <= 4'b0; INSTRUCTION_ADDRESS <= { 4'b0 , ADDRESS_INPUT-16'd1 }; INSTRUCTION_LOCATION <= ADDRESS_INPUT; func <= 1; jump_req <= 0; if (biu_state==`BIU_READ) biu_state <= `BIU_NEXT_ACTION; end else begin case(biu_state) `BIU_HALT: begin end `BIU_NEXT_ACTION: begin /* decide if we can read, if we are full or if we need to do something else */ if (write_request) begin func<=0; DATA_ADDRESS <= { 4'b0 , ADDRESS_INPUT }; DATA_DIR <= 1 ; VALID_INSTRUCTION <= 0; IOMEM <= MEM_OR_IO; biu_state <= (Wbit==0) ? `BIU_PUT_BYTE : (ADDRESS_INPUT[0:0]?`BIU_PUT_UNALIGNED_16BIT_DATA:`BIU_PUT_ALIGNED_16BIT_DATA) ; end else if ( read_request ) begin func<=0; DATA_ADDRESS <= { 4'b0 , ADDRESS_INPUT }; VALID_INSTRUCTION <= 0; DATA_DIR <= 0; read <=0; biu_state <= (ADDRESS_INPUT[0:0])?`BIU_GET_UNALIGNED_DATA:`BIU_GET_ALIGNED_DATA; end else begin if((FIFO_end-FIFO_start)>4'b0011)begin VALID_INSTRUCTION <= 1; INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start]; INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+4'd1]; INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+4'd2]; INSTRUCTION[ 7: 0] <= INPUT_FIFO[FIFO_start+4'd3]; end if ( FIFO_end[3:0]+4'b0001 != FIFO_start[3:0] ) begin func<=1; biu_state <= `BIU_READ; INSTRUCTION_ADDRESS <= INSTRUCTION_ADDRESS+1; write <= 1; read <= 0; IOMEM <= 0; BHE <= 0; end else begin biu_state <= `BIU_NEXT_ACTION; end end end /*************** INSTRUCTION FIFO READ ***************/ `BIU_READ: begin /* verilator lint_off BLKSEQ */ if(INSTRUCTION_ADDRESS[0:0]==0)begin INPUT_FIFO[FIFO_end] = external_data_bus[7:0]; end else begin INPUT_FIFO[FIFO_end] = external_data_bus[15:8]; end /* verilator lint_on BLKSEQ */ FIFO_end <= FIFO_end+1; biu_state <= `BIU_NEXT_ACTION; read<=1; end /*************** DATA WRITE ***************/ //TODO TODO TODO flush fifo, self modifying code `BIU_PUT_UNALIGNED_16BIT_DATA:begin BHE <= 0; data_bus_output_register <= {DATA[7:0],DATA[15:8]}; biu_state <= `BIU_PUT_UNALIGNED_PREP_NEXT; end `BIU_PUT_UNALIGNED_PREP_NEXT:begin write <= 0; biu_state <= `BIU_PUT_UNALIGNED_PREP_NEXT2; end `BIU_PUT_UNALIGNED_PREP_NEXT2:begin write <= 1; DATA_ADDRESS <= DATA_ADDRESS+1; BHE <= 1; biu_state <= `BIU_WRITE_EXIT; end `BIU_PUT_ALIGNED_16BIT_DATA:begin data_bus_output_register <= {DATA[15:8],DATA[7:0]}; biu_state <= `BIU_WRITE_EXIT; end `BIU_PUT_BYTE:begin biu_state <= `BIU_WRITE_EXIT; if(ADDRESS_INPUT[0:0]==0) begin BHE <= 1; data_bus_output_register <= {8'b0,DATA[7:0]}; end else begin BHE <= 0; data_bus_output_register <= {DATA[7:0],8'b0}; end end `BIU_WRITE_EXIT:begin write <= 0; biu_state <= `BIU_WRITE_RELEASE; end `BIU_WRITE_RELEASE:begin write <= 1; biu_state <= `BIU_NEXT_ACTION; end /*************** DATA READ ***************/ `define finished_read \ DATA_DIR <= 0; \ VALID_DATA <= 1;\ if ( read_request == 0 ) begin \ biu_state <= `BIU_NEXT_ACTION;\ VALID_DATA <= 0;\ end `BIU_GET_ALIGNED_DATA:begin DATA_OUT <= (Wbit==1)? external_data_bus : {8'b0,external_data_bus[7:0]} ; read <=1; `finished_read end `BIU_GET_UNALIGNED_DATA:begin DATA_OUT[7:0] <= external_data_bus[15:8]; read <=1; if(Wbit==1) begin biu_state <= `BIU_GET_SECOND_BYTE; end else begin `finished_read end end `BIU_GET_SECOND_BYTE:begin DATA_ADDRESS <= DATA_ADDRESS+1; biu_state <= `BIU_GET_SECOND_BYTE1; read <=0; end `BIU_GET_SECOND_BYTE1:begin DATA_OUT[15:8] <= external_data_bus[7:0]; `finished_read read <=1; end /*************** HOUSE KEEPING ***************/ `BIU_RESET: begin /* verilator lint_off BLKSEQ */ FIFO_start = 4'b0; /* verilator lint_on BLKSEQ */ FIFO_end <= 4'b0; biu_state <= `BIU_NEXT_ACTION; INSTRUCTION_ADDRESS <= 20'h0FFEF; INSTRUCTION_LOCATION <= 16'hFFF0; VALID_INSTRUCTION <= 0; VALID_DATA <= 0; DATA_DIR <= 0; end default: begin biu_state <= `BIU_NEXT_ACTION;/*Should be unreachable*/ end endcase end end wire [2:0] Isize; InstrSize InstrSize({INSTRUCTION[31:24],INSTRUCTION[21:19]},Isize); always @( NEXT_POSITION ) begin case(NEXT_POSITION) 2'b00:begin end /* no action */ 2'b01:begin /* Next instruction */ /* verilator lint_off BLKSEQ */ FIFO_start = FIFO_start + {1'b0,Isize}; INSTRUCTION_LOCATION <= INSTRUCTION_LOCATION + {12'b0,Isize};; /* verilator lint_on BLKSEQ */ if((FIFO_end-FIFO_start)>4'b0011)begin VALID_INSTRUCTION <= 1; INSTRUCTION[31:24] <= INPUT_FIFO[FIFO_start]; INSTRUCTION[23:16] <= INPUT_FIFO[FIFO_start+4'd1]; INSTRUCTION[15: 8] <= INPUT_FIFO[FIFO_start+4'd2]; INSTRUCTION[ 7: 0] <= INPUT_FIFO[FIFO_start+4'd3]; end else begin VALID_INSTRUCTION <= 0; end end 2'b10:begin /* Jump to specific location based on register */ jump_req <= 1; VALID_INSTRUCTION <= 0; end 2'b11:begin /* Jump to absolute location */ end endcase end endmodule /* Pre-Decode the instruction size */ /* IN: {CIR[15:8],CIR[5:3]} */ /* OUT: number in bytes */ module InstrSize ( input [10:0] IN, output reg [2:0] VERDICT ); always @( IN ) begin casez(IN) 11'b0000_010?_??? : VERDICT <= 3'd2+{2'b0,IN[3:3]}; /* ADD - Add Immediate word/byte to accumulator */ 11'b1000_00??_101 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* SUB - Subtract immediate word/byte from register/memory */ 11'b1000_00??_000 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* ADD - Add Immediate word/byte to register/memory */ 11'b1000_00??_111 : VERDICT <= 3'd3+{2'b0,(IN[4:3]==2'b01)}; /* CMP - compare Immediate with register / memory */ 11'b1011_????_??? : VERDICT <= 3'd2+{2'b0,IN[6:6]}; /* MOV - Move Immediate byte to register */ 11'b1000_10??_??? : VERDICT <= 3'd2; /* MOV - Reg/Mem to/from register */ 11'b0100_????_??? : VERDICT <= 3'd1; /* DEC - Decrement Register | INC - Increment Register */ 11'b1111_111?_00? : VERDICT <= 3'd2; /* INC - Register/Memory | DEC - Register/Memory */ 11'b1111_0100_??? : VERDICT <= 3'd1; /* HLT - Halt */ 11'b0011_110?_??? : VERDICT <= 3'd2+{2'b0,IN[3:3]}; /* CMP - Compare Immediate with accumulator */ 11'b0111_????_??? : VERDICT <= 3'd2; /* Conditional relative jumps ( JE/JZ, JS/JNS ... ) */ 11'b1110_1011_??? : VERDICT <= 3'd2; /* JMP - Unconditional jump direct within segment (short) */ 11'b1110_1000_??? : VERDICT <= 3'd3; /* CALL - Direct call within segment */ 11'b1100_0011_??? : VERDICT <= 3'd1; /* RET - Return from call within segment */ 11'b1010_101?_??? : VERDICT <= 3'd1; /* STOS - Write byte/word to [DI] and increment accordingly */ 11'b0101_0???_??? : VERDICT <= 3'd1; /* PUSH - SP-=2; [SP]=REG */ 11'b1111_011?_000 : VERDICT <= 3'd3+{2'b0,IN[3:3]}; /* TEST - Bitwise AND affecting only flags */ 11'b0101_1???_??? : VERDICT <= 3'd1; /* POP - REG=[SP]; SP+=2 */ 11'b1111_1111_100 : VERDICT <= 3'd2; /* JMP - Unconditional indirect within segment jump */ 11'b1100_011?_000 : VERDICT <= 3'd3+{2'b0,IN[3:3]}; /* MOV - Move immediate to register/memory */ 11'b1100_1101_??? : VERDICT <= 3'd2; /* INT - execute interrupt handler */ 11'b1110_011?_??? : VERDICT <= 3'd2; /* OUT - write AL or AX to a defined output port */ 11'b1100_1111_??? : VERDICT <= 3'd1; /* IRET - Return from interrupt */ default:begin end endcase end endmodule