406 lines
10 KiB
C
406 lines
10 KiB
C
#include "snes.h"
|
|
|
|
//only need this file if connector is present on the device
|
|
#ifdef SNES_CONN
|
|
|
|
//=================================================================================================
|
|
//
|
|
// SNES operations
|
|
// This file includes all the snes functions possible to be called from the snes dictionary.
|
|
//
|
|
// See description of the commands contained here in shared/shared_dictionaries.h
|
|
//
|
|
//=================================================================================================
|
|
|
|
/* Desc:Function takes an opcode which was transmitted via USB
|
|
* then decodes it to call designated function.
|
|
* shared_dict_snes.h is used in both host and fw to ensure opcodes/names align
|
|
* Pre: Macros must be defined in firmware pinport.h
|
|
* opcode must be defined in shared_dict_snes.h
|
|
* Post:function call complete.
|
|
* Rtn: SUCCESS if opcode found and completed, error if opcode not present or other problem.
|
|
*/
|
|
uint8_t snes_call( uint8_t opcode, uint8_t miscdata, uint16_t operand, uint8_t *rdata )
|
|
{
|
|
|
|
#define RD_LEN 0
|
|
#define RD0 1
|
|
#define RD1 2
|
|
|
|
#define BYTE_LEN 1
|
|
#define HWORD_LEN 2
|
|
|
|
switch (opcode) {
|
|
//no return value:
|
|
case SNES_SET_BANK:
|
|
HADDR_SET( operand );
|
|
break;
|
|
case SNES_ROM_WR:
|
|
snes_wr( operand, miscdata, 0 ); //last arg is romsel state
|
|
break;
|
|
case SNES_SYS_WR:
|
|
snes_wr( operand, miscdata, 1 ); //last arg is romsel state
|
|
break;
|
|
case FLASH_WR_5V:
|
|
snes_5v_flash_wr( operand, miscdata );
|
|
break;
|
|
case FLASH_WR_3V:
|
|
snes_3v_flash_wr( operand, miscdata );
|
|
break;
|
|
|
|
//8bit return values:
|
|
case SNES_ROM_RD:
|
|
rdata[RD_LEN] = BYTE_LEN;
|
|
rdata[RD0] = snes_rd( operand, 0 ); //last arg is romsel state
|
|
break;
|
|
case SNES_SYS_RD:
|
|
rdata[RD_LEN] = BYTE_LEN;
|
|
rdata[RD0] = snes_rd( operand, 1 ); //last arg is romsel state
|
|
break;
|
|
default:
|
|
//macro doesn't exist
|
|
return ERR_UNKN_SNES_OPCODE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
/* Desc:SNES ROM Read without changing high bank
|
|
* /ROMSEL set based on romsel arg
|
|
* EXP0/RESET not affected
|
|
* NOTE: this will access addresses when /ROMSEL isn't low on the console
|
|
* Pre: snes_init() setup of io pins
|
|
* Post:address left on bus
|
|
* data bus left clear
|
|
* Rtn: Byte read from ROM at addr
|
|
*/
|
|
uint8_t snes_rd( uint16_t addr, uint8_t romsel )
|
|
{
|
|
uint8_t read; //return value
|
|
|
|
//set address bus
|
|
ADDR_SET(addr);
|
|
|
|
if (romsel==0)
|
|
ROMSEL_LO();
|
|
|
|
CSRD_LO();
|
|
|
|
//couple more NOP's waiting for data
|
|
//zero nop's returned previous databus value
|
|
NOP(); //one nop got most of the bits right
|
|
NOP(); //two nop got all the bits right
|
|
NOP(); //add third nop for some extra
|
|
NOP(); //one more can't hurt
|
|
//might need to wait longer for some carts...
|
|
//this was long enough for AVR
|
|
|
|
//SNES v2.0p needed 6 more NOPs compared to v3.x & v1.x
|
|
//seems like a crazy long time...
|
|
NOP(); //v2.0p gets prod & density ID correct with addition of this NOP
|
|
//not sure why manf ID and sector ID are so much slower on v2 board
|
|
NOP();
|
|
NOP(); //v2.0p gets most bits right after 3 NOPs
|
|
NOP();
|
|
NOP(); //more after 5 extra...
|
|
NOP(); //all after 6 extra..
|
|
//sounds like 1 AVR NOP needs to equal 2STM32
|
|
//AVR running at 16Mhz, STM32 running at 48Mhz (3x as fast)
|
|
NOP(); //4MB proto needed this to get manfID, sector still bad
|
|
NOP(); //all good on 4MB proto
|
|
NOP(); //swapped for OR gate and takes a little longer now..?
|
|
|
|
//latch data
|
|
DATA_RD(read);
|
|
|
|
//return bus to default
|
|
CSRD_HI();
|
|
ROMSEL_HI();
|
|
|
|
return read;
|
|
}
|
|
|
|
/* Desc:SNES ROM Write
|
|
* /ROMSEL set based on romsel arg
|
|
* EXP0/RESET unaffected
|
|
* write value to currently selected bank
|
|
* NOTE: this will access addresses when /ROMSEL isn't low on the console
|
|
* Pre: snes_init() setup of io pins
|
|
* Post:data latched by anything listening on the bus
|
|
* address left on bus
|
|
* Rtn: None
|
|
*/
|
|
void snes_wr( uint16_t addr, uint8_t data, uint8_t romsel )
|
|
{
|
|
|
|
ADDR_SET(addr);
|
|
|
|
//put data on bus
|
|
DATA_OP();
|
|
DATA_SET(data);
|
|
|
|
//set /WR low first as this sets direction of
|
|
//level shifter on v3.0 boards
|
|
CSWR_LO();
|
|
//Then set romsel as this enables output of level shifter
|
|
if (romsel==0)
|
|
ROMSEL_LO();
|
|
//Doing the other order creates bus conflict between ROMSEL low -> WR low
|
|
|
|
//give some time
|
|
NOP();
|
|
NOP();
|
|
NOP(); //3x total NOPs fails ~2Bytes per 2MByte on v3.0 proto and inl6
|
|
//swaping /WR /ROMSEL order above helped greatly
|
|
//but still had 2 byte fails adding NOPS
|
|
NOP(); //4x total NOPs passed all bytes v3.0 SNES and inl6
|
|
NOP();
|
|
NOP(); //6x total NOPs passed all bytes
|
|
NOP();
|
|
NOP();
|
|
|
|
|
|
//latch data to cart memory/mapper
|
|
CSWR_HI();
|
|
ROMSEL_HI();
|
|
|
|
//Free data bus
|
|
DATA_IP();
|
|
}
|
|
|
|
/* Desc:SNES ROM Write to current address
|
|
* /ROMSEL set based on romsel arg
|
|
* EXP0/RESET unaffected
|
|
* write value to currently selected bank, and current address
|
|
* Mostly used when address is don't care
|
|
* Pre: snes_init() setup of io pins
|
|
* Post:data latched by anything listening on the bus
|
|
* address left on bus
|
|
* Rtn: None
|
|
*/
|
|
void snes_wr_cur_addr( uint8_t data, uint8_t romsel)
|
|
{
|
|
|
|
// ADDR_SET(addr);
|
|
|
|
//put data on bus
|
|
DATA_OP();
|
|
DATA_SET(data);
|
|
|
|
//set /WR low first as this sets direction of
|
|
//level shifter on v3.0 boards
|
|
CSWR_LO();
|
|
//Then set romsel as this enables output of level shifter
|
|
if (romsel==0)
|
|
ROMSEL_LO();
|
|
//Doing the other order creates bus conflict between ROMSEL low -> WR low
|
|
|
|
//give some time
|
|
NOP();
|
|
NOP();
|
|
NOP(); //3x total NOPs fails ~2Bytes per 2MByte on v3.0 proto and inl6
|
|
//swaping /WR /ROMSEL order above helped greatly
|
|
//but still had 2 byte fails adding NOPS
|
|
NOP(); //4x total NOPs passed all bytes v3.0 SNES and inl6
|
|
//NOP();
|
|
//NOP(); //6x total NOPs passed all bytes
|
|
|
|
//latch data to cart memory/mapper
|
|
CSWR_HI();
|
|
ROMSEL_HI();
|
|
|
|
//Free data bus
|
|
DATA_IP();
|
|
}
|
|
|
|
|
|
/* Desc:SNES ROM Page Read with optional USB polling
|
|
* /ROMSEL based on romsel arg, EXP0/RESET unaffected
|
|
* if poll is true calls usbdrv.h usbPoll fuction
|
|
* this is needed to keep from timing out when double buffering usb data
|
|
* Pre: snes_init() setup of io pins
|
|
* num_bytes can't exceed 256B page boundary
|
|
* Post:address left on bus
|
|
* data bus left clear
|
|
* data buffer filled starting at first to last
|
|
* Rtn: Index of last byte read
|
|
*/
|
|
uint8_t snes_page_rd_poll( uint8_t *data, uint8_t addrH, uint8_t romsel, uint8_t first, uint8_t len, uint8_t poll )
|
|
{
|
|
uint8_t i;
|
|
|
|
//set address bus
|
|
ADDRH(addrH);
|
|
|
|
//set /ROMSEL and /RD
|
|
CSRD_LO();
|
|
|
|
if (romsel==0) {
|
|
ROMSEL_LO();
|
|
}
|
|
|
|
//set lower address bits
|
|
ADDRL(first); //doing this prior to entry and right after latching
|
|
//gives longest delay between address out and latching data
|
|
for( i=0; i<=len; i++ ) {
|
|
//testing shows that having this if statement doesn't affect overall dumping speed
|
|
if ( poll == FALSE ) {
|
|
NOP(); //couple more NOP's waiting for data
|
|
NOP(); //one prob good enough considering the if/else
|
|
NOP();
|
|
NOP();
|
|
} else {
|
|
usbPoll(); //Call usbdrv.h usb polling while waiting for data
|
|
NOP();
|
|
NOP();
|
|
NOP();
|
|
}
|
|
|
|
//gameboy needed some extra NOPS
|
|
//I cut these back out because didn't want the delay in SNES
|
|
// NOP();
|
|
// NOP();
|
|
// NOP();
|
|
// NOP();
|
|
// NOP();
|
|
// NOP();
|
|
//latch data
|
|
DATA_RD(data[i]);
|
|
|
|
//set lower address bits
|
|
//ADDRL(++first); THIS broke things, on stm adapter because macro expands it twice!
|
|
first++;
|
|
ADDRL(first);
|
|
}
|
|
|
|
//return bus to default
|
|
CSRD_HI();
|
|
ROMSEL_HI();
|
|
|
|
//return index of last byte read
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Desc:SNES 5v ROM FLASH Write
|
|
* NOTE: /ROMSEL is always taken low
|
|
* NOTE: if the byte isn't erased it will stop over current value
|
|
* NOTE: doesn't hang if write fails, just returns, goal is to be fast
|
|
* Pre: snes_init() setup of io pins
|
|
* desired bank must already be selected
|
|
* Post:Byte written and ready for another write
|
|
* Rtn: None
|
|
*/
|
|
void snes_5v_flash_wr( uint16_t addr, uint8_t data )
|
|
{
|
|
|
|
uint8_t rv;
|
|
|
|
//unlock and write data
|
|
snes_wr(0x5555, 0xAA, 0);
|
|
snes_wr(0x2AAA, 0x55, 0);
|
|
snes_wr(0x5555, 0xA0, 0);
|
|
snes_wr(addr, data, 0);
|
|
|
|
do {
|
|
rv = snes_rd(addr, 0);
|
|
usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data
|
|
} while (rv != snes_rd(addr, 0));
|
|
|
|
return;
|
|
}
|
|
|
|
/* Desc:SNES 3v ROM FLASH Write
|
|
* NOTE: /ROMSEL is always taken low
|
|
* NOTE: if the byte isn't erased it will stop over current value
|
|
* NOTE: doesn't hang if write fails, just returns, goal is to be fast
|
|
* Pre: snes_init() setup of io pins
|
|
* desired bank must already be selected
|
|
* Post:Byte written and ready for another write
|
|
* Rtn: None
|
|
*/
|
|
void snes_3v_flash_wr( uint16_t addr, uint8_t data )
|
|
{
|
|
|
|
uint8_t rv;
|
|
|
|
//unlock and write data
|
|
snes_wr(0x8AAA, 0xAA, 0);
|
|
snes_wr(0x8555, 0x55, 0);
|
|
snes_wr(0x8AAA, 0xA0, 0);
|
|
snes_wr(addr, data, 0);
|
|
|
|
do {
|
|
rv = snes_rd(addr, 0);
|
|
usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data
|
|
} while (rv != snes_rd(addr, 0));
|
|
|
|
return;
|
|
}
|
|
|
|
/* Desc:SNES 3v ROM FLASH VERIFY Write
|
|
* NOTE: /ROMSEL is always taken low
|
|
* NOTE: if the byte isn't erased it will stop over current value
|
|
* NOTE: doesn't hang if write fails, just returns, goal is to be fast
|
|
* Pre: snes_init() setup of io pins
|
|
* desired bank must already be selected
|
|
* Post:Byte written and ready for another write
|
|
* Rtn: None
|
|
*/
|
|
uint8_t snes_3v_verify_wr( uint16_t addr, uint8_t data )
|
|
{
|
|
|
|
uint8_t rv;
|
|
|
|
//unlock and write data
|
|
snes_wr(0x8AAA, 0xAA, 0);
|
|
snes_wr(0x8555, 0x55, 0);
|
|
snes_wr(0x8AAA, 0xA0, 0);
|
|
snes_wr(addr, data, 0);
|
|
|
|
do {
|
|
rv = snes_rd(addr, 0);
|
|
usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data
|
|
} while (rv != snes_rd(addr, 0));
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Desc:SNES 3v ROM FLASH BUFFER Write 32Bytes at a time
|
|
* NOTE: /ROMSEL is always taken low
|
|
* NOTE: if the byte isn't erased it will stop over current value
|
|
* NOTE: doesn't hang if write fails, just returns, goal is to be fast
|
|
* Pre: snes_init() setup of io pins
|
|
* desired bank must already be selected
|
|
* Post:Byte written and ready for another write
|
|
* Rtn: None
|
|
*/
|
|
void snes_3v_buffer_wr( uint16_t addr, uint8_t *data )
|
|
{
|
|
|
|
/* TODO, actually implement this, currently everything is done on flash.c side
|
|
uint8_t rv;
|
|
|
|
//unlock and write data
|
|
snes_wr(0x8AAA, 0xAA, 0);
|
|
snes_wr(0x8555, 0x55, 0);
|
|
//write buffer write to SA
|
|
snes_wr(addr, 0x25, 0);
|
|
//write number of words - 1 to SA
|
|
|
|
//write first data to first address
|
|
snes_wr(addr, data[1], 0);
|
|
|
|
do {
|
|
rv = snes_rd(addr, 0);
|
|
usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data
|
|
} while (rv != snes_rd(addr, 0));
|
|
|
|
return;
|
|
*/
|
|
}
|
|
|
|
|
|
#endif //SNES_CONN
|