mirror of
https://github.com/MiSTer-devel/MegaCD_MiSTer.git
synced 2026-04-19 03:04:28 +00:00
107 lines
3.5 KiB
Systemverilog
107 lines
3.5 KiB
Systemverilog
// Cheat Code handling by Kitrinx
|
|
// Apr 21, 2019
|
|
|
|
// Code layout:
|
|
// {clock bit, code flags, 32'b address, 32'b compare, 32'b replace}
|
|
// 128 127:96 95:64 63:32 31:0
|
|
// Integer values are in BIG endian byte order, so it up to the loader
|
|
// or generator of the code to re-arrange them correctly.
|
|
|
|
module CODES(
|
|
input clk, // Best to not make it too high speed for timing reasons
|
|
input reset, // This should only be triggered when a new rom is loaded or before new codes load, not warm reset
|
|
input enable,
|
|
output available,
|
|
input [128:0] code,
|
|
input [ADDR_WIDTH - 1:0] addr_in,
|
|
input [DATA_WIDTH - 1:0] data_in,
|
|
output [DATA_WIDTH - 1:0] data_out
|
|
);
|
|
|
|
parameter ADDR_WIDTH = 16; // Not more than 32
|
|
parameter DATA_WIDTH = 8; // Not more than 32
|
|
parameter MAX_CODES = 32;
|
|
parameter BIG_ENDIAN = 0;
|
|
|
|
localparam INDEX_SIZE = $clog2(MAX_CODES-1); // Number of bits for index, must accomodate MAX_CODES
|
|
|
|
localparam DATA_S = DATA_WIDTH - 1;
|
|
localparam COMP_S = DATA_S + DATA_WIDTH;
|
|
localparam ADDR_S = COMP_S + ADDR_WIDTH;
|
|
localparam COMP_F_S = ADDR_S + 1;
|
|
localparam CODE_WIDTH = COMP_F_S + 1;
|
|
localparam ENA_F_S = CODE_WIDTH + 1;
|
|
|
|
localparam NO_ADDR_LSB = (DATA_WIDTH == 16) ? 1 : 0;
|
|
|
|
reg [ENA_F_S:0] codes[MAX_CODES];
|
|
|
|
wire [ADDR_WIDTH-1: 0] code_addr = code[64+:ADDR_WIDTH] ^ BIG_ENDIAN[0];
|
|
wire [DATA_WIDTH-1: 0] code_compare = code[32+:DATA_WIDTH];
|
|
wire [DATA_WIDTH-1: 0] code_data = code[0+:DATA_WIDTH];
|
|
wire code_comp_f = code[96];
|
|
wire code_width = code[97] && (DATA_WIDTH == 16);
|
|
|
|
// If MAX_INDEX is changes, these need to be made larger
|
|
wire [INDEX_SIZE-1:0] index, dup_index;
|
|
reg [INDEX_SIZE:0] next_index;
|
|
wire found_dup;
|
|
|
|
assign index = found_dup ? dup_index : next_index[INDEX_SIZE-1:0];
|
|
|
|
// See if the code exists already, so it can be disabled if loaded again
|
|
always_comb begin
|
|
int x;
|
|
dup_index = 0;
|
|
found_dup = 0;
|
|
|
|
for (x = 0; x < MAX_CODES; x = x + 1) begin
|
|
if (codes[x][ADDR_S-:ADDR_WIDTH] == code_addr) begin
|
|
dup_index = x[INDEX_SIZE-1:0];
|
|
found_dup = 1;
|
|
end
|
|
end
|
|
end
|
|
|
|
assign available = |next_index;
|
|
|
|
reg code_change;
|
|
always_ff @(posedge clk) begin
|
|
int x;
|
|
if (reset) begin
|
|
next_index <= 0;
|
|
code_change <= 0;
|
|
for (x = 0; x < MAX_CODES; x = x + 1) codes[x] <= '0;
|
|
end else begin
|
|
code_change <= code[128];
|
|
if (code[128] && ~code_change && (found_dup || next_index < MAX_CODES)) begin // detect posedge
|
|
// replace it if the same address, otherwise, add a new code
|
|
codes[index] <= {1'b1, code_width, code_comp_f, code_addr, code_compare, code_data};
|
|
if (~found_dup) next_index <= next_index + 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
int x;
|
|
data_out = data_in;
|
|
|
|
if (enable) begin
|
|
for (x = 0; x < MAX_CODES; x = x + 1) begin
|
|
if (codes[x][ENA_F_S] && codes[x][ADDR_S-:(ADDR_WIDTH-NO_ADDR_LSB)] == addr_in[ADDR_WIDTH-1:NO_ADDR_LSB]) begin
|
|
if (!codes[x][COMP_F_S] || (
|
|
(DATA_WIDTH == 8 || !codes[x][CODE_WIDTH]) ? (data_in == codes[x][COMP_S-:DATA_WIDTH]) :
|
|
(codes[x][ADDR_S-ADDR_WIDTH+1]) ? (data_in[15:8] == codes[x][(COMP_S-DATA_WIDTH+1) +:8]) :
|
|
(data_in[7:0] == codes[x][(COMP_S-DATA_WIDTH+1) +:8]) ))
|
|
begin
|
|
if(DATA_WIDTH == 8 || !codes[x][CODE_WIDTH]) data_out = codes[x][DATA_S-:DATA_WIDTH];
|
|
else if (codes[x][ADDR_S-ADDR_WIDTH+1]) data_out[15:8] = codes[x][(DATA_S-DATA_WIDTH+1) +:8];
|
|
else data_out[7:0] = codes[x][(DATA_S-DATA_WIDTH+1) +:8];
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|