Skip to content

Files

Latest commit

Oct 19, 2021
08c6461 · Oct 19, 2021

History

History

detect2600_verilator

2600 Detector

Verilator Video

The original 2600 core didn't autodetect carts. Instead it used a wildcard in the config string, and it looked at the file extension to pick a mapper. This meant that each cartridge needed to be named correctly.

The Stella Cart Detector uses a bunch of heuristics to search through the cartridge for known opcodes to detect the cart type. Another list of mappers

In C:

bool CartDetector::isProbablyCV(const ByteBuffer& image, size_t size)
{
  // CV RAM access occurs at addresses $f3ff and $f400
  // These signatures are attributed to the MESS project
  uInt8 signature[2][3] = {
    { 0x9D, 0xFF, 0xF3 },  // STA $F3FF.X
    { 0x99, 0x00, 0xF4 }   // STA $F400.Y
  };
  if(searchForBytes(image, size, signature[0], 3))
    return true;
  else
    return searchForBytes(image, size, signature[1], 3);
}


bool CartDetector::searchForBytes(const uInt8* image, size_t imagesize,
                                  const uInt8* signature, uInt32 sigsize,
                                  uInt32 minhits)
{
  uInt32 count = 0;

  for(uInt32 i = 0; i < imagesize - sigsize; ++i)
  {
    uInt32 j;

    for(j = 0; j < sigsize; ++j)
    {
      if(image[i + j] != signature[j])
        break;
    }
    if(j == sigsize)
    {
      if(++count == minhits)
        break;
      i += sigsize;  // skip past this signature 'window' entirely
    }
  }

  return (count == minhits);
}

in verilog, we can write a similar module, but it is going to look at a byte stream instead of reading through memory. We can do all the checks in parallel by instantiating a lot of matching modules.

we use parameters in the module to configure it and pass in the bytes we are looking for.

here is the module:

//------------------------------
// CV detector
//-------------------------------

wire hasMatchCV_0 , hasMatchCV_1;
wire hasMatchCV = hasMatchCV_0 | hasMatchCV_1;


match_bytes #(
        .num_bytes(8'd3),
        .pattern({ 8'h9D, 8'hFF , 8'hF3 }),
        .needmatches(8'd1)
        ) match_bytes_CV_0(
        .addr(addr),
        .enable(enable),
        .clk(clk),
        .reset(reset),
        .data(data),
        .hasMatch(hasMatchCV_0)
);
match_bytes #(
        .num_bytes(8'd3),
        .pattern({ 8'h99, 8'h00 , 8'hF4 }),
        .needmatches(8'd1)
        ) match_bytes_CV_1(
        .addr(addr),
        .enable(enable),
        .clk(clk),
        .reset(reset),
        .data(data),
        .hasMatch(hasMatchCV_1)
);

module match_bytes
(
        input clk,
        input reset,
        input  [15:0] addr,
        input  enable,
        input  [7:0] data,     // data in,
        output reg hasMatch
);

parameter [7:0] num_bytes;
parameter [(num_bytes*8)-1:0] pattern;
parameter [7:0] needmatches=8'b1;

reg [(num_bytes*8)-1:0] lastPattern;

reg [7:0] curMatch;

always @(posedge clk)
begin
        if (enable)
        begin
                // use address 0 as reset since reset is high during cart loads
                if (addr==16'b0)
                begin
                        curMatch<=8'b0;
                        hasMatch<=0;
                        lastPattern<= {lastPattern[(num_bytes*8)-9:0],data};
                end
                else
                begin
                        lastPattern<= {lastPattern[(num_bytes*8)-9:0],data};
                        if (lastPattern == pattern)
                        begin
                                        curMatch<=curMatch+8'b1;
                                        if (curMatch==(needmatches-8'b1))
                                                hasMatch<=1;
                        end
                end
        end
        if (reset) begin
                curMatch <= 0;
                hasMatch <= 0;
                lastPattern <= '0;
        end
end

endmodule: match_bytes

Testbench

Now that we have some code, we would like to see if it autodetects the cartridges correctly. We can use verilator to load a cartridge and feed it to our detect routine. We will create a simple top that is called from verilator, and instantiates our 2600 detector. We put in a few lines of help so that the code will print out a comma delimited string, and set a done flag when it is finished. This makes things a bit simpler.

We can pass md5sum a list of files, and it will create md5 checksums and filenames. A quick copy/replace and we can turn it into a CSV file. We use a python program to read this file and a spreadsheet we found on the web to load and check each rom, and dump out the results.