4-mb Ram Cart

Is there any way to incorporate in to home dev. I'm planning to work on a ngpc emu but I want to use the whole system ram for the emu and other stuff and the 4 mb ram cart for roms, but i dont want to make it if i cant use the 4-mb ram cart.
 
Just check for the presence of the cart (if you want to make it easy for people with the faulty carts sold now, check both the cart id and the amount of memory present) then just use it.
 
Originally posted by black@Jun 30, 2004 @ 02:22 AM

...I want to use the whole system ram for the emu and other stuff and the 4 mb ram cart for roms...

Hey, what a great idea!

There is the SEGA_MEM library, for which you define an area of RAM (Cartridge, ...) and then you can use this area for dynamic allocation via malloc. The library has it's own malloc, free,... functions. And you can only define one such area. I doubt you would need multiple areas on Saturn, but little modification could overcome this.
 
1. How to use External RAM cartridge

2. How to use half of CPU internal cache as RAM

1. Based on the ST-TECH-47.pdf manual and SCU user manual for memory mapping, i coded a sample to init the external RAM Cartridge:

Code:
int initCartridge(unsigned char cs)

{

  Uint32 id, setReg, *ptr, i, size, ok;

//!!!!!! on real Saturn, it doesn't go beyound this check

// on emu, always reports id == 0

  // cartridge initialization: as in ST-TECH-47.pdf

  // 1. verify cartridge id

  // 5A - 8 MB

  // 5C - 32 MB

  id = *((Uint32 *)0x24ffffff);

  if(id == 0x5a) {

    slPrint("8 MB RAM cart detected!", slLocate(2,5));

  } else if(id == 0x5c) {

    slPrint("32 MB RAM cart detected!", slLocate(2,5));

  } else {   

    // 2. prompt proper connection

    slPrint("There is no external RAM cartridge!", slLocate(2,5));

    slPrintHex(id, slLocate(5,6));

//    return 0;

  }

  slPrintHex(id, slLocate(5,6));

  // 3. write 1 to initialize

  *((Uint32 *)0x257efffe) = 1;

  // 4. set A-bus set and refresh registers

  setReg = 0;

  if(cs == 0) {

    // set cs0 for 32MBit

    setReg |= 1 << 29; // After-READ precharge insert bit

    ptr = (Uint32 *)0x22000000;

    size = 1048576;

  } else {

    // set cs1 for 16MBit ??

    setReg |= 1 << 28; // external wait effective bit

    ptr = (Uint32 *)0x24000000;

    size = 262144;

  }

  setReg |= 3 << 24; // Burst cycle wait number

  setReg |= 3 << 20; // Normal cycle wait number

  *((Uint32 *)0x25fe0080) = setReg;

  // 5. verify data before proceeding

    // write

  for(i = 0; i < size; i++)

    *(ptr++) = i;

    // read

  ok = 1;

  for(i = 0; i < size; i++) {

    if(*(ptr++) != i) {

      ok = 0;

    }  

  } 

    

  slPrint("verifying RAM cartridge:", slLocate(2,7));

  slPrintHex(ok, slLocate(5,8)); 

                 

  return ok;

}

Now the problem is that it freezes on real Saturn and always reports id = 0 in Satourne emu.

Furthermore, the manual states that area 0x22400000 - 0x227FFFFF is allocated to the cartridge, but I cannot find this elsewhere, it's not on SCU's memory map....

2. I know how to set 2kB of the 4kB cpu cache as local RAM:

If the cache is set to 2-way mode(instead of 4-way mode), it is set to 2kB RAM, too. The SEGA_CSH library can be taken to do this: CSH_SET_WAY_MODE( CSH_2WAY )

But I don't know how to address this RAM directly!

Now that I had another look into CSH_MAIN.C of SBL's cache lib, I see that CSH_init() writes zeros to locations after 0x60000000 and performs CSH_SET_ACS_WAY(way) 4 times.

Could this have something to do with the RAM location?
 
Reading a 32-bit int from 0x24ffffff isn't going to net you more than an alignment exception. The memory range in the cartridge manual is the correct one, and the SH7604 manual will have info on using the cache as RAM.
 
Furthermore, the manual states that area 0x22400000 - 0x227FFFFF is allocated to the cartridge, but I cannot find this elsewhere, it's not on SCU's memory map....

Cartridges are mapped to the "A-Bus CS0" (normally ROM/RAM) and "A-Bus CS1" (normally cart ID) regions.
 
I'm very, very happy to say:

Both problems are solved! :banana :cheers

(thanks antime and ExCyber!)

1. checking for, initializing and using extended RAM carts:

Code:
//  RB_initRAMCart(Uint8 cs, Uint8 quiet)

//  - checks for present RAM cart, inits and verifies it

//

//  inputs:

//      cs          -   select whether to set CS0 or CS1 space 

//                      (both works, don't know where the difference is)   

//      quiet       -   don't print messages

//

//  returns:

//      cartridge id    -   on success

//                          (8MB: 0x5a or 32MB: 0x5c)

//      -1              -   no cart

//      -2              -   data verify failed 

Sint16 RB_CartRAM_init(Uint8 cs, Uint8 quiet)

{

    Uint32 setReg, refReg, *DRAM0, *DRAM1, DRAMsize, i, ok;

    Uint8 id;

    // cartridge initialization: as in ST-TECH-47.pdf

    // 1. verify cartridge id

    // 5A - 8 MB

    // 5C - 32 MB

    //! is last byte of A-BUS CS1 area

    id = *((Uint8 *)0x24ffffff);

    if(id == 0x5a) {

        DRAMsize = 0x80000;

        if(quiet == 0)

            slPrint("8 MB RAM cart detected!", slLocate(2,5));

    } else if(id == 0x5c) {

        DRAMsize = 0x200000;

        if(quiet == 0)

            slPrint("32 MB RAM cart detected!", slLocate(2,5));

    } else {      

        // 2. prompt proper connection

        if(quiet == 0) {

            slPrint("The extended RAM", slLocate(2,5));

            slPrint("cartridge is not", slLocate(2,6));

            slPrint("inserted properly.", slLocate(2,7));

            slPrint("Please turn off", slLocate(2,9));

            slPrint("power and reinsert", slLocate(2,10));

            slPrint("the extended RAM", slLocate(2,11));

            slPrint("cartridge.", slLocate(2,12));

        }    

        return -1;

    }

    // 3. write 1 to initialize

    // is at the end of A-BUS Dummy area

    // !!! word format !!! (but what is meant?)

    *((Uint16 *)0x257efffe) = 1;

    

    // 4. set A-bus set and refresh registers

    setReg = refReg = 0;

    if(cs == 0) {

        // set cs0 for 32MBit

        setReg |= 1 << 29;  // After-READ precharge insert bit

        setReg |= 3 << 24;  // Burst cycle wait number

        setReg |= 3 << 20;  // Normal cycle wait number

    } else {

        // set cs1 for 16MBit ??

        setReg |= 1 << 28;  // external wait effective bit

        setReg |= 15 << 24; // Burst cycle wait number

        setReg |= 15 << 20; // Normal cycle wait number

    }

    *((Uint32 *)0x25fe00B0) = setReg;

        // A-Bus refresh register

    refReg |= 1 << 4;       // A-Bus refresh enable bit

            // I've just take this value

            // see SCU user manual for value range

    refReg |= 1;            // A-Bus refresh cycle wait

    *((Uint32 *)0x25fe00B8) = refReg;

    

    // 5. verify data before proceeding

        // cache-through DRAM0 and DRAM1

    DRAMsize >>= 2; // byte length -> longword length

    DRAM0 = (Uint32 *)0x22400000;

    DRAM1 = (Uint32 *)0x22600000;

        // write

    for(i = 0; i < DRAMsize; i++) {

        *(DRAM0 + i) = i;

        *(DRAM1 + i) = DRAMsize - 1 - i;

    }    

        // read

    ok = 1;

    for(i = 0; i < DRAMsize; i++) {

        if(*(DRAM0 + i) != i) {

            were_here("DRAM0 %i != %i", *(DRAM0 + i), i);

            ok = 0;

        }    

        if(*(DRAM1 + i) != (DRAMsize - 1 - i)) {

            were_here("DRAM1 %i != %i", *(DRAM1 + i), (DRAMsize - 1 - i));

            ok = 0;

        }    

    } 

       

    if(ok == 1) {                        

        if(quiet == 0)

            slPrint("verifying RAM cart OK!", slLocate(2,7));

        return id;

    } else {

        if(quiet == 0)

            slPrint("verifying RAM cart FAILED!", slLocate(2,7));

        return -2;

    }    

}

2. initializing and using half of CPU's Cache as RAM:

init:

CSH_SET_WAY_MODE( CSH_2WAY )

Edit: better use CSH_Init(CSH_2WAY), it cares about cache disabling and stuff

now ways 0, 1 are RAM,

ways 2,3 mixed instruction/data cache memory

use:

0xC0000000

way 0

0xC00003FF

0xC0000400

way 1

0xC00007FF

Wow, I'm very excited about that! Now borders are shifted back...

Edit: look at 2.
 
/*******************************************************************
* Jo Engine - Ultra Stable Native 16-Bit Dual Cartridge Tester
*******************************************************************/

#include <jo/jo.h>

/* Hardware Constants - Safe Probing Only */
#define CART_ID_ADDR ((volatile unsigned char *)0x24FFFFFF)
#define CART_INIT_ADDR ((volatile unsigned short *)0x257EFFFE)
#define A_BUS_SET_REG ((volatile unsigned long *)0x25FE0080)
#define A_BUS_REF_REG ((volatile unsigned long *)0x25FE0033)

/* Memory Map for Extended RAM (DRAM0) */
#define EXT_RAM_BASE 0x22400000
#define SIZE_1MB_BYTES (1 * 1024 * 1024)
#define SIZE_4MB_BYTES (4 * 1024 * 1024)

typedef enum {
TEST_STATE_IDLE = 0,
TEST_STATE_PASS1_WRITE,
TEST_STATE_PASS1_VERIFY,
TEST_STATE_COMPLETE_SUCCESS,
TEST_STATE_FAILED
} test_state_t;

/* Global Engine State Definitions */
static bool is_ram_cart_ready = false;
static test_state_t g_test_status = TEST_STATE_IDLE;
static unsigned long g_current_offset = 0;
static unsigned long g_failed_address = 0;
static unsigned short g_expected_val = 0;
static unsigned short g_actual_val = 0;

static unsigned long g_total_test_bytes = 0;
static unsigned long g_mirror_mask = 0;
static int g_display_expected_mb = 0;

/* Hardware spin-delay helper */
static void bus_delay(void)
{
for (volatile int i = 0; i < 0x200; i++) {
__asm__("nop");
}
}

#pragma GCC push_options
#pragma GCC optimize ("O0")

/* Baseline Hardware Identification and Safe Target Bus Init */
static bool _init_hardware_bus(void)
{
unsigned char cart_id = *CART_ID_ADDR;

if (cart_id == 0x5A || cart_id == 0x1A || cart_id == 0xDA) {
g_total_test_bytes = SIZE_1MB_BYTES;
g_mirror_mask = SIZE_1MB_BYTES - 1;
g_display_expected_mb = 1;

/* Force hardware refresh configurations strictly for physical 1MB layouts */
*CART_INIT_ADDR = 0x0001;
bus_delay();
*A_BUS_SET_REG = 0x23301FF0;
*A_BUS_REF_REG = 0x00000013;
bus_delay();

} else if (cart_id == 0x5C || cart_id == 0x1C || cart_id == 0xDC) {
g_total_test_bytes = SIZE_4MB_BYTES;
g_mirror_mask = SIZE_4MB_BYTES - 1;
g_display_expected_mb = 4;
/* Do NOT modify A_BUS registers to keep SAROO flashcarts functional */

} else {
return false;
}
return true;
}

void jo_dram_cart_init(void)
{
if (!_init_hardware_bus()) {
is_ram_cart_ready = false;
g_test_status = TEST_STATE_FAILED;
} else {
is_ram_cart_ready = true;
g_test_status = TEST_STATE_PASS1_WRITE;
g_current_offset = 0;
}
}

/* Sequential Non-Blocking 16-Bit Memory Validation Engine */
static void run_sequential_stress_test(void)
{
if (g_total_test_bytes == 0) return;

const unsigned long chunk_size = 64 * 1024; // 64KB parsed per loop cycle
unsigned long end_offset = g_current_offset + chunk_size;

if (end_offset > g_total_test_bytes) {
end_offset = g_total_test_bytes;
}

/* FIXED: Target the uncached region using native 16-bit shortward definitions */
volatile unsigned short *uncached_ram_16 = (volatile unsigned short *)(EXT_RAM_BASE | 0x20000000);

switch (g_test_status) {

case TEST_STATE_PASS1_WRITE:
/* Step by 2 bytes (16-bits) to match physical hardware layout dimensions */
for (; g_current_offset < end_offset; g_current_offset += 2) {
unsigned long target_index = (g_current_offset & g_mirror_mask) / 2;

/* Create a solid 16-bit alternating check sequence */
uncached_ram_16[target_index] = 0x5555;
}
if (g_current_offset >= g_total_test_bytes) {
g_current_offset = 0;
g_test_status = TEST_STATE_PASS1_VERIFY;
}
break;

case TEST_STATE_PASS1_VERIFY:
for (; g_current_offset < end_offset; g_current_offset += 2) {
unsigned long target_index = (g_current_offset & g_mirror_mask) / 2;
unsigned short read_val = uncached_ram_16[target_index];

if (read_val != 0x5555) {
g_failed_address = EXT_RAM_BASE + g_current_offset;
g_expected_val = 0x5555;
g_actual_val = read_val;
g_test_status = TEST_STATE_FAILED;
return;
}
}
if (g_current_offset >= g_total_test_bytes) {
g_test_status = TEST_STATE_COMPLETE_SUCCESS;
/* Expose the validated boundary pools back to Jo Engine */
jo_add_memory_zone((unsigned char *)EXT_RAM_BASE, g_total_test_bytes);
}
break;

default:
break;
}
}
#pragma GCC pop_options

void my_game_draw(void)
{
jo_printf(2, 2, "=== NATIVE 16-BIT RAM TESTER ===");

unsigned char current_id = *CART_ID_ADDR;
jo_printf(2, 4, "Detected Cartridge ID: 0x%02X", current_id);
jo_printf(2, 5, "Target Space Capacity: %d MB", g_display_expected_mb);

if (!is_ram_cart_ready && g_test_status == TEST_STATE_FAILED) {
jo_printf(2, 7, "Status: DETECT OPERATION ERROR");
return;
}

int current_kb = (int)(g_current_offset / 1024);
int total_kb = (int)(g_total_test_bytes / 1024);

switch (g_test_status) {
case TEST_STATE_PASS1_WRITE:
jo_printf(2, 7, "Status: STRESSING PASS (WRITE)... ");
jo_printf(2, 8, "Verified: %d KB / %d KB", current_kb, total_kb);
break;
case TEST_STATE_PASS1_VERIFY:
jo_printf(2, 7, "Status: STRESSING PASS (VERIFY)... ");
jo_printf(2, 8, "Verified: %d KB / %d KB", current_kb, total_kb);
break;
case TEST_STATE_COMPLETE_SUCCESS:
jo_printf(2, 7, "Status: SUCCESS! POOL IS OPERATIONAL ");
jo_printf(2, 8, "Result: %dMB Space fully operational.", g_display_expected_mb);
break;
case TEST_STATE_FAILED:
jo_printf(2, 7, "Status: !!! TEST VERIFICATION FAULT !!!");
jo_printf(2, 9, "Address: 0x%08X", (unsigned int)g_failed_address);
jo_printf(2, 10, "Expected: 0x%04X", g_expected_val);
jo_printf(2, 11,"Read Out: 0x%04X", g_actual_val);
break;
default:
break;
}

run_sequential_stress_test();
}

void jo_main(void)
{
jo_core_init(JO_COLOR_Black);
jo_dram_cart_init();
jo_core_add_callback(my_game_draw);
jo_core_run();
}
 
Back
Top