OriVEmu/rtl/i8255.sv
2024-09-30 01:21:31 +02:00

104 lines
2.6 KiB
Systemverilog

`timescale 1ps/1ps
module i8255
(
input wire i_clk,
input wire[1:0] i_addr,
input wire[7:0] i_data,
output wire[7:0] o_data,
input wire i_rd_n,
input wire i_wr_n,
input wire i_cs_n,
input wire i_reset,
input wire[7:0] i_PA,
output wire[7:0] o_PA,
input wire[7:0] i_PB,
output wire[7:0] o_PB,
input wire[7:0] i_PC,
output wire[7:0] o_PC
);
logic[7:0] reg_A;
logic[7:0] reg_B;
logic[7:0] reg_C;
logic[7:0] control_reg;
logic[7:0] Ain;
logic[7:0] Bin;
logic[7:0] Cin;
logic[3:0] Cinl;
logic[3:0] Cinh;
always_ff @(posedge i_clk)
begin
if (i_reset)
begin
reg_A <= '0;
reg_B <= '0;
reg_C <= '0;
control_reg <= 8'h9B;
end
else if ((!i_cs_n) & (!i_wr_n))
begin
case (i_addr)
2'b00: reg_A <= i_data;
2'b01: reg_B <= i_data;
2'b10: reg_C <= i_data;
2'b11:
begin
if (i_data[7] == 1'b1) // immediate value
control_reg <= i_data;
else // set/reset bit in port C
begin
if (i_data[1] == '1) // set bit
reg_C[i_data[3:1]] <= '1;
else // reset bit
reg_C[i_data[3:1]] <= '0;
end
end
endcase
end
end
logic[7:0] reg_o_A, reg_o_B, reg_o_C;
always_ff @(posedge i_clk)
begin
if (control_reg[4] == '0)
reg_o_A <= reg_A;
if (control_reg[1] == '0)
reg_o_B <= reg_B;
if (control_reg[0] == '0)
reg_o_C[3:0] <= reg_C[3:0];
if (control_reg[3] == '0)
reg_o_C[7:4] <= reg_C[7:4];
end;
assign o_PA = reg_o_A;
assign o_PB = reg_o_B;
assign o_PC = reg_o_C;
assign Cinl = control_reg[0] ? i_PC[3:0] : reg_C[3:0];
assign Bin = control_reg[1] ? i_PB : reg_B;
assign Cinh = control_reg[3] ? i_PC[7:4] : reg_C[7:4];
assign Ain = control_reg[4] ? i_PA : reg_A;
assign Cin = { Cinh, Cinl };
logic[7:0] r_data;
always_ff @(posedge i_clk)
begin
if ((!i_cs_n) & (!i_rd_n))
begin
case (i_addr)
2'b00: r_data <= Ain;
2'b01: r_data <= Bin;
2'b10: r_data <= Cin;
2'b11: r_data <= control_reg;
endcase;
end
end;
assign o_data = r_data;
endmodule