Cold War Computing

Retrocomputing Blog

1802 RetroShield – SPI RAM

In the original 1802 Monitor + Tiny Basic Arduino code by Erturk Kocalar, the memory map is divided into three sections: ROM, onboard RAM, and external/simulated RAM.

  • ROM is hardcoded in the Arduino’s program memory, is of variable length, and starts at address 0x0000.
  • Onboard RAM starts at 0x8000 and is 6K in length if SPI RAM is not used, or 2K in length if it is not. (The 4K difference is instead used for caching the SPI RAM.)
  • If SPI RAM is used, the remainder of the memory map to 0xFFFF is cached SPI RAM. If SPI RAM is not used, the remainder of the memory map to 0xFFFF is hardcoded to 0xFF.

Instructions for adding 128K SPI RAM to a RetroShield project can be found here, and a discussion of the caching scheme used in the default project is here.

My goal right now is to leave the functionality the same if SPI RAM is not used, but change the behavior when SPI RAM is used so that the full 64K memory map is cached SPI RAM. For now, the ROM will still be located in program memory, but will be copied into RAM at startup. The eventual goal is to have no ROM, instead having system software loaded from SD on startup.

The first change to the default project was to enable the SPI RAM:

////////////////////////////////////////////////////////////////////
// Options
//   USE_SPI_RAM: Enable Microchip 128KB SPI-RAM  (Details coming up)
//   USE_LCD_KEYPAD: Enable LCD/Keyboard Shield
//   outputDEBUG: Print memory access debugging messages.
//   TPB_SOFTWARE: Generate TPB internally so access SC0 signal. 
////////////////////////////////////////////////////////////////////
#define USE_SPI_RAM     1
#define USE_LCD_KEYPAD  0
#define outputDEBUG     0
#define TPB_SOFTWARE    1

Next, I changed the Memory Layout constants to match my desired layout:

////////////////////////////////////////////////////////////////////
// MEMORY LAYOUT
////////////////////////////////////////////////////////////////////

#if (USE_SPI_RAM)
  // 2K MEMORY
  //#define RAM_START   0x8000
  //#define RAM_END     0x87FF
  //byte    RAM[RAM_END-RAM_START+1];
  #define RAM_START 0x0000
  #define RAM_END   0xFFFF
#else
  // 6K MEMORY
  #define RAM_START   0x8000
  #define RAM_END     0x97FF
  byte    RAM[RAM_END-RAM_START+1];
#endif

I changed the code that reads memory so that if SPI RAM is enabled, all reads come from SPI RAM.

#if (USE_SPI_RAM)
    DATA_latched = cache_read_byte(uP_ADDR);
#else
    // ROM?
    if ( (ROM_START <= uP_ADDR) && (uP_ADDR <= ROM_END) )
    {
      DATA_latched = pgm_read_byte_near(rom_bin + (uP_ADDR - ROM_START));
    }
    else
    // Execute from RAM?
    if ( (RAM_START <= uP_ADDR) && (uP_ADDR <= RAM_END) )
      DATA_latched = RAM[uP_ADDR - RAM_START];
    else
      // Dummy 0xFF (eeprom style) out for unmapped memory locations
      DATA_latched = 0xFF;      
#endif

And the same for the code that writes memory.

    // Memory Write
#if (USE_SPI_RAM)
    cache_write_byte(uP_ADDR, DATA_latched);
#else
    if ( (RAM_START <= uP_ADDR) && (uP_ADDR <= RAM_END) )
      RAM[uP_ADDR - RAM_START] = DATA_latched;    
#endif

I added a function to copy the ROM from program memory to SPI-RAM.

#if (USE_SPI_RAM)
void rom_init() {
  unsigned int i;

  for(i=ROM_START; i<=ROM_END; i++) {
    cache_write_byte(i, pgm_read_byte_near(rom_bin + (i - ROM_START)));
  }

  Serial.print(i - ROM_START); Serial.println(" bytes of ROM copied to RAM.");
}
#endif

And finally, I added a call to this function inside the setup() function.

#if (USE_SPI_RAM)
  // Initialize memory subsystem
  spi_init();
  cache_init();
  rom_init();
#endif

0 thoughts on “1802 RetroShield – SPI RAM

Leave a Reply

Your email address will not be published. Required fields are marked *