diff --git a/firmware/source/dump.c b/firmware/source/dump.c index 7467853..1a9f4cd 100644 --- a/firmware/source/dump.c +++ b/firmware/source/dump.c @@ -32,6 +32,15 @@ uint8_t dump_buff( buffer *buff ) { buff->last_idx, ~FALSE ); break; case SNESROM: + addrH |= 0x80; //$8000 LOROM space + //need to split page_num + //A14-8 page_num[7-0] + //A15 high (LOROM) + //A23-16 page_num[14-8] + HADDR_SET( (buff->page_num)>>7 ); + buff->cur_byte = snes_rom_page_rd_poll( buff->data, addrH, buff->id, + //id contains MSb of page when <256B buffer + buff->last_idx, ~FALSE ); case SNESRAM: //warn addrX = ((buff->page_num)>>8); break; diff --git a/firmware/source/dump.h b/firmware/source/dump.h index 3b59ca6..4dde0dd 100644 --- a/firmware/source/dump.h +++ b/firmware/source/dump.h @@ -5,6 +5,7 @@ #include "types.h" #include "buffer.h" #include "nes.h" +#include "snes.h" #include "shared_dictionaries.h" #include "shared_errors.h" diff --git a/firmware/source/io.c b/firmware/source/io.c index 4bed0b2..496e0c8 100644 --- a/firmware/source/io.c +++ b/firmware/source/io.c @@ -28,7 +28,7 @@ uint8_t io_call( uint8_t opcode, uint8_t miscdata, uint16_t operand, uint8_t *rd switch (opcode) { case IO_RESET: io_reset(); break; case NES_INIT: nes_init(); break; -// case SNES_INIT: snes_init(); break; + case SNES_INIT: snes_init(); break; case EXP0_PULLUP_TEST: rdata[RD_LEN] = BYTE_LEN; rdata[RD0] = exp0_pullup_test(); break; @@ -152,11 +152,10 @@ void nes_init() } -/* //SNES cartridge interfacing setup //set outputs as required -//latch address of $000000 +//latch address of $00:0000 //disable cart memories //reset high disables SRAM and puts INL carts in PRGM mode //Excersize extreme caution calling this while NES/FC cart inserted @@ -176,28 +175,26 @@ void snes_init() CSWR_OP(); CSWR_HI(); - //disable SRAM and put cart in PRGM mode + //disable SRAM and put cart in PLAY mode EXP0_OP(); EXP0_HI(); //other control pins are inputs or unused, leave as IP pullup from reset //memories are now disabled Data bus should be clear - - //setup AHL FF - AHL_OP(); - AHL_CLK(); - //setup AXL FF - AXL_OP(); - AXL_CLK(); + DATA_ENABLE(); + DATA_IP_PU(); //now meet conditions to call other macros - //setup address $000000 + //setup address $0000 + ADDR_ENABLE(); ADDR_SET(0x0000); - ADDRX_SET(0x00); + + //setup HIGH ADDR with bank $00 + HADDR_ENABLE(); + HADDR_SET(0x00); } -*/ //Test starts by verifying EXP0 can be driven low, if not, will return one byte of AUX_PIN //followed by alternating 0xAA, 0x55, 0xAA... diff --git a/firmware/source/io.h b/firmware/source/io.h index c3b00e7..a775be7 100644 --- a/firmware/source/io.h +++ b/firmware/source/io.h @@ -9,7 +9,7 @@ uint8_t io_call( uint8_t opcode, uint8_t miscdata, uint16_t operand, uint8_t *rd void io_reset(); void nes_init(); -//void snes_init(); +void snes_init(); uint8_t exp0_pullup_test(); #endif diff --git a/firmware/source/pinport.c b/firmware/source/pinport.c index d084bcb..cf81217 100644 --- a/firmware/source/pinport.c +++ b/firmware/source/pinport.c @@ -383,6 +383,15 @@ uint8_t pinport_call( uint8_t opcode, uint8_t miscdata, uint16_t operand, uint8_ case EXP_DISABLE_: EXP_DISABLE(); break; case EXP_SET_: EXP_SET(operand); break; + //============================ + //HIGH ADDR PORT 8bit WIDE ACCESS + //opcode: type of operation + //operand: value to place on bus + //============================ + case HADDR_ENABLE_: HADDR_ENABLE(); break; + case HADDR_DISABLE_: HADDR_DISABLE(); break; + case HADDR_SET_: HADDR_SET(operand); break; + default: //macro doesn't exist or isn't on this PCB version return ERR_UNKN_PP_OPCODE; diff --git a/firmware/source/pinport_al.h b/firmware/source/pinport_al.h index c017aa1..75d3d5b 100644 --- a/firmware/source/pinport_al.h +++ b/firmware/source/pinport_al.h @@ -478,10 +478,11 @@ void software_AXL_CLK(); */ -#define RCC_AHBENR_CTL (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIODEN) -#define RCC_AHBENR_ADDR RCC_AHBENR_GPIOCEN -#define RCC_AHBENR_DATA RCC_AHBENR_GPIOBEN -#define RCC_AHBENR_EXP (RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOBEN) +#define RCC_AHBENR_CTL (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIODEN) +#define RCC_AHBENR_ADDR RCC_AHBENR_GPIOCEN +#define RCC_AHBENR_HADDR (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN) +#define RCC_AHBENR_DATA RCC_AHBENR_GPIOBEN +#define RCC_AHBENR_EXP (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN) #endif //STM_INL6 @@ -979,7 +980,7 @@ void software_AXL_CLK(); #define ADDR_EN_CLK() RCC->AHBENR |= RCC_AHBENR_ADDR #define ADDR_EN_FF() CTL_OP(AHLbank, AHL); CTL_SET_LO(AHLbank, AHL) - #define ADDR_ENABLE() ADDR_EN_CLK(); ADDR_EN_FF(); ADDR_OP() + #define ADDR_ENABLE() DATA_ENABLE(); ADDR_EN_CLK(); ADDR_EN_FF(); ADDR_OP() #endif //STM_ADAPTER @@ -998,7 +999,7 @@ void software_AXL_CLK(); #define ADDR_SET(hword) ADDRL(hword); ADDRH(hword>>8) #define ADDR_EN_FF() CTL_OP(AHLbank, AHL); CTL_SET_LO(AHLbank, AHL) - #define ADDR_ENABLE() ADDR_EN_FF(); ADDR_OP(); + #define ADDR_ENABLE() DATA_ENABLE(); ADDR_EN_FF(); ADDR_OP(); #endif //AVR_KAZZO @@ -1046,7 +1047,7 @@ void software_AXL_CLK(); #define EXP_ENABLE() ADDR_EN_CLK(); EXP_OP() #define EXP_DISABLE() EXP_PU(); EXP_IP() -//STM_INL6 +//end STM_INL6 #else //AVR_KAZZO or STM_ADAPTER @@ -1068,5 +1069,56 @@ void software_AXL_CLK(); #endif //AVR_KAZZO or STM_ADAPTER +// --------------------------------------------------------------------------------------- +// HIGH ADDRESS PORT 8bits A16-23 +// +// This port is present on all devices +// Restrictions: CANNOT be used when EXPANSION PORT is enabled +// CIRAM_A10 & CIRAM /CE cannot be used on CONTROL PORT +// Directionality: All pins are forced output +// Driver: All pins are push-pull +// Write/Output: Byte access only, no bit accesses +// Read/Input: Not supported +// +// INL6 dual purposes A22-23 for CIRAM_A10 & CIRAM_CE respectively +// EXP1-5, & 7 are mapped to A16-21 respectively +// +// AVR_KAZZO & STM_ADATPER use dual purpose the EXPANSION PORT for this PORT +// EXP1-8 map to A16-23 respectively +// +// --------------------------------------------------------------------------------------- + +#ifdef STM_INL6 + + //A16-21 are on PB10-15 these also map to EXP1-5, & 7 + //A22-23 are on PA9-10 these also map to CIRAM A10 & CIRAM /CE respectively + #define A16_21bank GPIOB + #define A22_23bank GPIOA + + #define HADDR_PU() A16_21bank->PUPDR |= (PUPDR_PU_ALL & 0xFFF00000); A22_23bank->PUPDR |= (PUPDR_PU_ALL & 0x0003C000) + #define HADDR_IP() A16_21bank->MODER &=~(MODER_OP_ALL & 0xFFF00000); A22_23bank->MODER &=~(MODER_OP_ALL & 0x0003C000) + #define HADDR_OP() A16_21bank->MODER |= (MODER_OP_ALL & 0xFFF00000); A22_23bank->MODER |= (MODER_OP_ALL & 0x0003C000) + + #define HADDR_SET(val) A16_21bank->ODR = ((A16_21bank->ODR&0x03FF) | (val<<10 & 0xFC00)); A22_23bank->ODR = ((A22_23bank->ODR & 0xF9FF) | (val<<3 & 0x0600)) + + #define HADDR_EN_CLK() RCC->AHBENR |= RCC_AHBENR_HADDR + #define HADDR_ENABLE() HADDR_EN_CLK(); HADDR_OP() + #define HADDR_DISABLE() HADDR_PU(); HADDR_IP() + +//end STM_INL6 +#else //AVR_KAZZO or STM_ADAPTER + + + // ADDR16-23 are behind AXL flipflop + + //clocks must be initialized, Data bus clear + #define HADDR_SET(val) EXP_SET(val) + + #define HADDR_EN_FF() EXP_EN_FF() + #define HADDR_ENABLE() EXP_ENABLE() + #define HADDR_DISABLE() EXP_DISABLE() + +#endif //AVR_KAZZO or STM_ADAPTER + #endif diff --git a/firmware/source/snes.c b/firmware/source/snes.c new file mode 100644 index 0000000..bb8bc16 --- /dev/null +++ b/firmware/source/snes.c @@ -0,0 +1,589 @@ +#include "snes.h" + +//================================================================================================= +// +// 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_rom_wr( operand, miscdata ); + break; + + //8bit return values: + case SNES_ROM_RD: + rdata[RD_LEN] = BYTE_LEN; + rdata[RD0] = snes_rom_rd( operand ); + break; + default: + //macro doesn't exist + return ERR_UNKN_SNES_OPCODE; + } + + return SUCCESS; + +} + +/* Desc:SNES ROM Read without changing high bank + * /ROMSEL always set low + * EXP0/RESET not affected + * 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_rom_rd( uint16_t addr ) +{ + uint8_t read; //return value + + //set address bus + ADDR_SET(addr); + + 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... + + //latch data + DATA_RD(read); + + //return bus to default + CSRD_HI(); + ROMSEL_HI(); + + return read; +} + +/* Desc:SNES ROM Write + * /ROMSEL always set low + * EXP0/RESET unaffected + * read value from currently selected bank + * Pre: snes_init() setup of io pins + * Post:data latched by anything listening on the bus + * address left on bus + * Rtn: None + */ +void snes_rom_wr( uint16_t addr, uint8_t data ) +{ + + ADDR_SET(addr); + + //put data on bus + DATA_OP(); + DATA_SET(data); + + //PRG R/W LO + ROMSEL_LO(); + CSWR_LO(); + + //give some time + NOP(); + NOP(); + + //latch data to cart memory/mapper + ROMSEL_HI(); + CSWR_HI(); + + //Free data bus + DATA_IP(); +} + +/* Desc:SNES ROM Page Read with optional USB polling + * /ROMSEL always low, 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_rom_page_rd_poll( uint8_t *data, uint8_t addrH, uint8_t first, uint8_t len, uint8_t poll ) +{ + uint8_t i; + + //set address bus + ADDRH(addrH); + + //set /ROMSEL and /RD + CSRD_LO(); + 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(); + } + //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: Discrete board PRG-ROM only write, does not write to mapper +// * PRG-ROM /WE <- EXP0 w/PU +// * PRG-ROM /OE <- /ROMSEL +// * PRG-ROM /CE <- GND +// * PRG-ROM write: /WE & /CE low, /OE high +// * mapper '161 CLK <- /ROMSEL +// * mapper '161 /LOAD <- PRG R/W +// * mapper '161 /LOAD must be low on rising edge of CLK to latch data +// * This is a /WE controlled write. Address latched on falling edge, +// * and data latched on rising edge EXP0 +// * Note:addrH bit7 has no effect (ends up on PPU /A13) +// * /ROMSEL, M2, & PRG R/W signals untouched +// * Pre: nes_init() setup of io pins +// * Post:data latched by PRG-ROM, mapper register unaffected +// * address left on bus +// * data left on bus, but pullup only +// * EXP0 left pulled up +// * Rtn: None +// */ +// void discrete_exp0_prgrom_wr( uint16_t addr, uint8_t data ) +// { +// ADDR_SET(addr); +// +// DATA_OP(); +// DATA_SET(data); +// +// EXP0_OP(); //Tas = 0ns, Tah = 30ns +// EXP0_LO(); +// EXP0_IP_PU(); //Twp = 40ns, Tds = 40ns, Tdh = 0ns +// //16Mhz avr clk = 62.5ns period guarantees timing reqts +// DATA_IP(); +// } +// +// // +// // /* Desc:Emulate NES CPU Read as best possible +// // * decode A15 from addrH to set /ROMSEL as expected +// // * float EXP0 +// // * toggle M2 as NES would +// // * insert some NOP's in to be slow like NES +// // * Note:not the fastest read operation +// // * Pre: nes_init() setup of io pins +// // * Post:address left on bus +// // * data bus left clear +// // * EXP0 left floating +// // * Rtn: Byte read from PRG-ROM at addrHL +// // */ +// // uint8_t emulate_nes_cpu_rd( uint8_t addrH, uint8_t addrL ) +// // { +// // uint8_t read; //return value +// // +// // //m2 should be low as it aids in disabling WRAM +// // //this is also m2 state at beginging of CPU cycle +// // //all these pins should already be in this state, but +// // //go ahead and setup just to be sure since we're trying +// // //to be as accurate as possible +// // _EXP0_FLT(); //this could have been left pulled up +// // _M2_LO(); //start of CPU cycle +// // _ROMSEL_HI(); //trails M2 +// // _PRGRW_RD(); //happens just after M2 +// // +// // //set address bus +// // ADDR_OUT = addrL; +// // _ADDRH_SET(addrH); +// // +// // //couple NOP's to wait a bit +// // NOP(); +// // NOP(); +// // +// // //set M2 and /ROMSEL +// // if( addrH >= 0x80 ) { //addressing cart rom space +// // _M2_HI(); +// // _ROMSEL_LO(); //romsel trails M2 during CPU operations +// // } else { +// // _M2_HI(); +// // } +// // +// // //couple more NOP's waiting for data +// // NOP(); +// // NOP(); +// // NOP(); +// // NOP(); +// // NOP(); +// // NOP(); +// // +// // //latch data +// // read = DATA_IN; +// // +// // //return bus to default +// // _M2_LO(); +// // _ROMSEL_HI(); +// // +// // return read; +// // } +// // +// /* Desc:NES CPU Read without being so slow +// * decode A15 from addrH to set /ROMSEL as expected +// * float EXP0 +// * toggle M2 as NES would +// * Pre: nes_init() setup of io pins +// * Post:address left on bus +// * data bus left clear +// * EXP0 left floating +// * Rtn: Byte read from PRG-ROM at addrHL +// */ +// uint8_t nes_cpu_rd( uint16_t addr ) +// { +// uint8_t read; //return value +// +// //set address bus +// ADDR_SET(addr); +// +// //set M2 and /ROMSEL +// MCO_HI(); +// if( addr >= 0x8000 ) { //addressing cart rom space +// ROMSEL_LO(); //romsel trails M2 during CPU operations +// } +// +// //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... +// +// //latch data +// DATA_RD(read); +// +// //return bus to default +// MCO_LO(); +// ROMSEL_HI(); +// +// return read; +// } +// +// /* Desc:NES CPU Write +// * Just as you would expect NES's CPU to perform +// * A15 decoded to enable /ROMSEL +// * This ends up as a M2 and/or /ROMSEL controlled write +// * Note:addrH bit7 has no effect (ends up on PPU /A13) +// * EXP0 floating +// * Pre: nes_init() setup of io pins +// * Post:data latched by anything listening on the bus +// * address left on bus +// * data left on bus, but pullup only +// * Rtn: None +// */ +// void nes_cpu_wr( uint16_t addr, uint8_t data ) +// { +// //Float EXP0 as it should be in NES +// EXP0_IP_FL(); +// +// //need for whole function +// //_DATA_OP(); +// +// //set addrL +// //ADDR_OUT = addrL; +// //latch addrH +// //DATA_OUT = addrH; +// //_AHL_CLK(); +// ADDR_SET(addr); +// +// //PRG R/W LO +// PRGRW_LO(); +// +// //put data on bus +// DATA_OP(); +// DATA_SET(data); +// +// //set M2 and /ROMSEL +// MCO_HI(); +// if( addr >= 0x8000 ) { //addressing cart rom space +// ROMSEL_LO(); //romsel trails M2 during CPU operations +// } +// +// //give some time +// NOP(); +// NOP(); +// +// //latch data to cart memory/mapper +// MCO_LO(); +// ROMSEL_HI(); +// +// //retore PRG R/W to default +// PRGRW_HI(); +// +// //Free data bus +// DATA_IP(); +// } +// +// /* Desc:NES PPU Read +// * decode A13 from addrH to set /A13 as expected +// * Pre: nes_init() setup of io pins +// * Post:address left on bus +// * data bus left clear +// * Rtn: Byte read from CHR-ROM/RAM at addrHL +// */ +// uint8_t nes_ppu_rd( uint16_t addr ) +// { +// uint8_t read; //return value +// +// //addr with PPU /A13 +// if (addr < 0x2000) { //below $2000 A13 clear, /A13 set +// addr |= PPU_A13N_WORD; +// } //above PPU $1FFF, A13 set, /A13 clear +// +// ADDR_SET( addr ); +// +// //set CHR /RD and /WR +// 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... +// +// //latch data +// DATA_RD(read); +// +// //return bus to default +// CSRD_HI(); +// +// return read; +// } +// +// /* Desc:NES PPU Write +// * decode A13 from addrH to set /A13 as expected +// * flash: address clocked falling edge, data rising edge of /WE +// * Pre: nes_init() setup of io pins +// * Post:data written to addrHL +// * address left on bus +// * data bus left clear +// * Rtn: None +// */ +// +// void nes_ppu_wr( uint16_t addr, uint8_t data ) +// { +// +// //addr with PPU /A13 +// if (addr < 0x2000) { //below $2000 A13 clear, /A13 set +// addr |= PPU_A13N_WORD; +// } //above PPU $1FFF, A13 set, /A13 clear +// +// ADDR_SET( addr ); +// +// //put data on bus +// DATA_OP(); +// DATA_SET(data); +// +// NOP(); +// +// //set CHR /RD and /WR +// CSWR_LO(); +// +// //might need to wait longer for some carts... +// NOP(); //one can't hurt +// +// //latch data to memory +// CSWR_HI(); +// +// //clear data bus +// DATA_IP(); +// +// } +// +// +// /* Desc:PPU CIRAM A10 NT arrangement sense +// * Toggle A11 and A10 and read back CIRAM A10 +// * report back if vert/horiz/1scnA/1scnB +// * reports nesdev defined mirroring +// * does not report Nintendo's "Name Table Arrangement" +// * Pre: nes_init() setup of io pins +// * Post:address left on bus +// * Rtn: MIR_VERT, MIR_HORIZ, MIR_1SCNA, MIR_1SCNB +// * errors not really possible since all combinations +// * of CIRAM A10 level designate something valid +// */ +// uint8_t ciram_a10_mirroring( void ) +// { +// uint16_t readV, readH; +// +// //set A10, clear A11 +// ADDRH(A10_BYTE); +// CIA10_RD(readV); +// +// //set A11, clear A10 +// ADDRH(A11_BYTE); +// CIA10_RD(readH); +// +// //if CIRAM A10 was always low -> 1 screen A +// if ((readV==0) & (readH==0)) return MIR_1SCNA; +// //if CIRAM A10 was always hight -> 1screen B +// if ((readV!=0) & (readH!=0)) return MIR_1SCNB; +// //if CIRAM A10 toggled with A10 -> Vertical mirroring, horizontal arrangement +// if ((readV!=0) & (readH==0)) return MIR_VERT; +// //if CIRAM A10 toggled with A11 -> Horizontal mirroring, vertical arrangement +// if ((readV==0) & (readH!=0)) return MIR_HORZ; +// +// //shouldn't be here... +// return GEN_FAIL; +// } +// +// /* Desc:NES CPU Page Read with optional USB polling +// * decode A15 from addrH to set /ROMSEL as expected +// * float EXP0 +// * toggle M2 as NES would +// * if poll is true calls usbdrv.h usbPoll fuction +// * this is needed to keep from timing out when double buffering usb data +// * Pre: nes_init() setup of io pins +// * num_bytes can't exceed 256B page boundary +// * Post:address left on bus +// * data bus left clear +// * EXP0 left floating +// * data buffer filled starting at first to last +// * Rtn: Index of last byte read +// */ +// uint8_t nes_cpu_page_rd_poll( uint8_t *data, uint8_t addrH, uint8_t first, uint8_t len, uint8_t poll ) +// { +// uint8_t i; +// +// //set address bus +// ADDRH(addrH); +// +// //set M2 and /ROMSEL +// MCO_HI(); +// if( addrH >= 0x80 ) { //addressing cart rom space +// ROMSEL_LO(); //romsel trails M2 during CPU operations +// } +// +// //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 +// } else { +// usbPoll(); //Call usbdrv.h usb polling while waiting for data +// } +// //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 +// MCO_LO(); +// ROMSEL_HI(); +// +// //return index of last byte read +// return i; +// } +// +// /* Desc:NES PPU Page Read with optional USB polling +// * decode A13 from addrH to set /A13 as expected +// * if poll is true calls usbdrv.h usbPoll fuction +// * this is needed to keep from timing out when double buffering usb data +// * Pre: nes_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 for len number of bytes +// * Rtn: Index of last byte read +// */ +// uint8_t nes_ppu_page_rd_poll( uint8_t *data, uint8_t addrH, uint8_t first, uint8_t len, uint8_t poll ) +// { +// uint8_t i; +// +// if (addrH < 0x20) { //below $2000 A13 clear, /A13 set +// //ADDRH(addrH | PPU_A13N_BYTE); +// //Don't do weird stuff like above! logic inside macro expansions can have weird effects!! +// addrH |= PPU_A13N_BYTE; +// ADDRH(addrH); +// } else { //above PPU $1FFF, A13 set, /A13 clear +// ADDRH(addrH); +// } +// +// //set CHR /RD and /WR +// CSRD_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++ ) { +// //couple more NOP's waiting for data +// if ( poll == FALSE ) { +// NOP(); //one prob good enough considering the if/else +// NOP(); +// } else { +// usbPoll(); +// } +// //latch data +// DATA_RD(data[i]); +// //set lower address bits +// first ++; +// ADDRL(first); +// } +// +// //return bus to default +// CSRD_HI(); +// +// //return index of last byte read +// return i; +// } diff --git a/firmware/source/snes.h b/firmware/source/snes.h new file mode 100644 index 0000000..ebe2188 --- /dev/null +++ b/firmware/source/snes.h @@ -0,0 +1,15 @@ +#ifndef _snes_h +#define _snes_h + +#include "pinport.h" +#include "buffer.h" //TODO remove this junk when get rid of FALSE +#include "shared_dictionaries.h" +#include "shared_errors.h" + +uint8_t snes_call( uint8_t opcode, uint8_t miscdata, uint16_t operand, uint8_t *rdata ); +uint8_t snes_rom_rd( uint16_t addr ); +void snes_rom_wr( uint16_t addr, uint8_t data ); +uint8_t snes_rom_page_rd_poll( uint8_t *data, uint8_t addrH, uint8_t first, uint8_t len, uint8_t poll ); + + +#endif diff --git a/firmware/source/usb.c b/firmware/source/usb.c index afa9255..079d7eb 100644 --- a/firmware/source/usb.c +++ b/firmware/source/usb.c @@ -113,6 +113,10 @@ uint16_t usbFunctionSetup(uint8_t data[8]) { rv[RETURN_ERR_IDX] = nes_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] ); break; //end of NES + case DICT_SNES: + rv[RETURN_ERR_IDX] = snes_call( spacket->opcode, spacket->miscdata, spacket->operand, &rv[RETURN_LEN_IDX] ); + break; //end of NES + case DICT_BUFFER: //just give buffer.c the setup packet and let it figure things out for itself @@ -120,10 +124,6 @@ uint16_t usbFunctionSetup(uint8_t data[8]) { break; //end of BUFFER /* - case DICT_SNES: - //break; //end of SNES - - case DICT_USB: //currently just a simple way to read back usbFunctionWrite status SUCCESS/ERROR //if there are future status' to read back may have to create some functions diff --git a/firmware/source/usb.h b/firmware/source/usb.h index 10906c1..e105375 100644 --- a/firmware/source/usb.h +++ b/firmware/source/usb.h @@ -15,13 +15,13 @@ #include "pinport.h" #include "io.h" #include "nes.h" +#include "snes.h" #include "buffer.h" #include "types.h" #include "shared_usb.h" #include "shared_errors.h" #include "shared_dictionaries.h" -//#include "snes.h" #define ENDPOINT_BIT 0x80 //Bit 7 of bmRequest type determines endpoint #define ENDPOINT_IN 0x80 //In: device-to-host. diff --git a/host/scripts/app/cart.lua b/host/scripts/app/cart.lua index 34e7e51..be0d8c2 100644 --- a/host/scripts/app/cart.lua +++ b/host/scripts/app/cart.lua @@ -5,6 +5,7 @@ local cart = {} -- import required modules local dict = require "scripts.app.dict" local nes = require "scripts.app.nes" +local snes = require "scripts.app.snes" -- file constants @@ -48,14 +49,26 @@ local function detect_console( debug ) -- //if couldn't detect NES/FC check for SNES cartridge -- //want to keep from outputting on EXP bus if NES cart was found --- if ( cart->console == UNKNOWN ) { --- //only way I can think of is if memory is enabled by certain addresses and control signals --- snes_init( transfer ); --- if ( snes_mem_visible( transfer, 0x00, 0xFFFC )) { --- //CHECK for memory visible near NES reset vector --- debug("SNES memories detected"); --- cart->console = SNES_CART; --- } + if cart_console == nil then + + --currently detect SNES cartridge by reading reset vector + --ensuring it's valid by range and differing between banks + dict.io("SNES_INIT") + + local bank0vect = snes.read_reset_vector( 0, debug ) --actual reset vector + local bank1vect = snes.read_reset_vector( 1, debug ) + --bank 0 and bank 1 would have same reset vector on a NES cart + --these probably differ on a SNES if there's more than 32/64KB of ROM. + + if bank0vect ~= bank1vect then + if (bank0vect >= 0x8000) and (bank0vect < 0xFFFA) then + if debug then print("valid SNES reset vector found that differs between bank0 & bank1") end + cart_console = "SNES" + end + else + if debug then print("invalid SNES reset vector or same vector found on bank0 & bank1") end + end + -- //now it's possible that rom is there, but data is 0xFF so above test would fail -- //one option would be to drive bus low for short period and see if bus can be -- //driven low. This could damage pin drivers though, best to create command in @@ -66,6 +79,7 @@ local function detect_console( debug ) -- -- //playable SNES carts should have data somewhere in reset vector... -- } + end -- -- //always end with resetting i/o dict.io("IO_RESET") diff --git a/host/scripts/app/dict.lua b/host/scripts/app/dict.lua index d420138..b92458e 100644 --- a/host/scripts/app/dict.lua +++ b/host/scripts/app/dict.lua @@ -319,6 +319,61 @@ local function nes( opcode, operand, misc, data ) end +end + + +-- external call for snes dictionary +local function snes( opcode, operand, misc, data ) + + if not op_snes[opcode] then + print("ERROR undefined opcode:", opcode, "must be defined in shared_dict_snes.h") + return nil + end + + if not operand then + operand = 0 + elseif type(operand) == "string" then + if not op_snes[operand] then + print("ERROR undefined operand:", operand, "must be defined in shared_dict_snes.h") + return nil + end + --decode string operands into + operand = op_snes[operand] + end + + if not misc then misc = 0 end + + local wLength, ep = default_rlen_1_in(op_snes[opcode.."rlen"]) + + local count + count, data = usb_vend_xfr( + -- ep, dictionary wValue[misc:opcode] wIndex wLength data + ep, dict["DICT_SNES"], ( misc<<8 | op_snes[opcode]), operand, wLength, data) + --print(count) + local error_code, data_len + if ep == USB_IN then + error_code = data:byte(RETURN_ERR_IDX) + data_len = data:byte(RETURN_LEN_IDX) + end + --print("error:", error_code, "data_len:", data_len) + + assert ( (error_code == err_codes["SUCCESS"]), "ERROR!!! device error code:", error_code) + --if error_code ~= err_codes["SUCCESS"] then + -- print("ERROR!!! device error code:", error_code) + --end + + if data_len and data_len ~= (wLength - RETURN_LEN_IDX) then + print("WARNING!! Device's return data length:", data_len, "did not match expected:", wLength-RETURN_LEN_IDX) + end + + --process the return data string and return it to calling function + if data_len then + return string_to_int( data:sub(RETURN_DATA, data_len+RETURN_DATA), data_len) + else + return nil + end + + end local function buffer_payload_in( wLength, buff_num ) @@ -510,13 +565,14 @@ create_dict_tables( op_buffer, "../shared/shared_dict_buffer.h") create_dict_tables( op_io, "../shared/shared_dict_io.h") create_dict_tables( op_operation, "../shared/shared_dict_operation.h") create_dict_tables( op_nes, "../shared/shared_dict_nes.h") ---create_dict_tables( op_snes, "../shared/shared_dict_snes.h") +create_dict_tables( op_snes, "../shared/shared_dict_snes.h") create_dict_tables( err_codes, "../shared/shared_errors.h") -- functions other modules are able to call dict.pinport = pinport dict.io = io dict.nes = nes +dict.snes = snes dict.buffer = buffer dict.buffer_payload_in = buffer_payload_in dict.buffer_payload_out = buffer_payload_out diff --git a/host/scripts/app/dump.lua b/host/scripts/app/dump.lua index 4428344..f768f06 100644 --- a/host/scripts/app/dump.lua +++ b/host/scripts/app/dump.lua @@ -232,6 +232,108 @@ local function dump_nes( file, debug ) end +local function dump_snes( file, mapping, debug ) + + local buff0 = 0 + local buff1 = 1 + local cur_buff_status = 0 + local data = nil --lua stores data in strings + + if mapping ~= "LOROM" then + print("currently only support LOROM SNES mapping!!!") + end + + if debug then print("dumping cart") end + + --start with reset and init + dict.io("IO_RESET") + dict.io("SNES_INIT") + + --setup buffers and manager + dict.operation("SET_OPERATION", op_buffer["RESET"] ) + --reset buffers first + dict.buffer("RAW_BUFFER_RESET") + + --need to allocate some buffers for dumping + --2x 128Byte buffers + local num_buffers = 2 + local buff_size = 128 + print("allocating buffers") + assert(buffers.allocate( num_buffers, buff_size ), "fail to allocate buffers") + + --set buffer elements as needed + --set reload which gets added to page_num after each buffer read + --set reload to 256 = 1 when translated to page_num (done in allocate buffers funct) + --set page_num to non-zero if offset arg sent + --set mem_type and part_num to designate how to get/write data + print("setting map n part") + dict.buffer("SET_MEM_N_PART", (op_buffer["SNESROM"]<<8 | op_buffer["MASKROM"]), buff0 ) + dict.buffer("SET_MEM_N_PART", (op_buffer["SNESROM"]<<8 | op_buffer["MASKROM"]), buff1 ) + + --set multiple and add_mult only when flashing + --set mapper, map_var, and function to designate read/write algo + + print("setting map n mapvar") + dict.buffer("SET_MAP_N_MAPVAR", (op_buffer[mapping]<<8 | op_buffer["NOVAR"]), buff0 ) + dict.buffer("SET_MAP_N_MAPVAR", (op_buffer[mapping]<<8 | op_buffer["NOVAR"]), buff1 ) + +-- //tell buffers what function to use for dumping +-- //TODO when start implementing other mappers + + --debugging print out buffer elements + --print("\nget operation:") + --dict.operation("GET_OPERATION" ) + --print("\n\ngetting cur_buff status") + --dict.buffer("GET_CUR_BUFF_STATUS" ) + --print("\n\ngetting elements") + --dict.buffer("GET_PRI_ELEMENTS", nil, buff0 ) + --dict.buffer("GET_PRI_ELEMENTS", nil, buff1 ) + --dict.buffer("GET_SEC_ELEMENTS", nil, buff0 ) + --dict.buffer("GET_SEC_ELEMENTS", nil, buff1 ) + --dict.buffer("GET_PAGE_NUM", nil, buff0 ) + --dict.buffer("GET_PAGE_NUM", nil, buff1 ) + + print("\n\nsetting operation STARTDUMP"); + --inform buffer manager to start dumping operation now that buffers are initialized + dict.operation("SET_OPERATION", op_buffer["STARTDUMP"] ) + + --need these calls to delay things a bit to let first buffer dump complete.. + --wait for first buffer to finish dumping before calling payload + buffers.status_wait({buff0}, {"DUMPED"}) + + print("starting first payload"); + --now just need to call series of payload IN transfers to retrieve data + for i=1, (2048*1024/buff_size) do + file:write( dict.buffer_payload_in( buff_size )) + end + + print("payload done"); + --buffer manager updates from USB_UNLOADING -> DUMPING -> DUMPED + --while one buffer is unloading, it sends next buffer off to dump + --payout opcode updates from DUMPED -> USB_LOADING + --so end result is buff0 will get sent off to dump extra data that's not needed + --but we never call payload on it, so buff1 never moves from USB_UNLOADING + --but it has to be done unloading if we sent a subsequent setup packet + --buffers.status_wait({buff0, buff1}, {"DUMPED","USB_UNLOADING"}) + --in reality I don't think this wait is needed. Because we've already gotten our data if + --we're at this point.. + --what we really need to do is set the buffer's status' to a reset state + --that way we can give them new instructions before restarting DUMP of CHR portion + --best way can think of doing this is a operation "RESET" which updates buffer status, + --without deallocating them. In reality should be able to do this by setting operation to reset. + --and having STARTDUMP/FLASH initialize buffer status' as well. + dict.operation("SET_OPERATION", op_buffer["RESET"] ) + dict.buffer("RAW_BUFFER_RESET") + + --close file in main + --reset io at end + dict.operation("SET_OPERATION", op_buffer["RESET"] ) + dict.buffer("RAW_BUFFER_RESET") + dict.io("IO_RESET") + + return true +end + -- global variables so other modules can use them @@ -240,6 +342,7 @@ end -- functions other modules are able to call dump.dump_nes = dump_nes +dump.dump_snes = dump_snes -- return the module's table return dump diff --git a/host/scripts/app/snes.lua b/host/scripts/app/snes.lua new file mode 100644 index 0000000..81c8a34 --- /dev/null +++ b/host/scripts/app/snes.lua @@ -0,0 +1,106 @@ + +-- create the module's table +local snes = {} + +-- import required modules +local dict = require "scripts.app.dict" + +-- file constants +local RESET_VECT_HI = 0xFFFD +local RESET_VECT_LO = 0xFFFC + +-- local functions + +-- Desc:read reset vector from passed in bank +-- Pre: snes_init() been called to setup i/o +-- Post:Address left on bus memories disabled +-- Rtn: reset vector that was found +local function read_reset_vector( bank, debug ) + + --ensure cart is in play mode + dict.pinport("CTL_SET_HI", "SNES_RST") + + --first set SNES bank A16-23 + dict.snes("SNES_SET_BANK", bank) + + --read reset vector high byte + vector = dict.snes("SNES_ROM_RD", RESET_VECT_HI) + --shift high byte of vector to where it belongs + vector = vector << 8 + --read low byte of vector + vector = vector | dict.snes("SNES_ROM_RD", RESET_VECT_LO) + + if debug then print("SNES bank:", bank, "reset vector", string.format("$%x", vector) ) end + + return vector +end + +-- Desc: attempt to read flash rom ID +-- Pre: snes_init() been called to setup i/o +-- Post:Address left on bus memories disabled +-- Rtn: true if flash ID found +local function read_flashID( debug ) + + local rv + --enter software mode A11 is highest address bit that needs to be valid + --datasheet not exactly explicit, A11 might not need to be valid + --part has A-1 (negative 1) since it's in byte mode, meaning the part's A11 is actually A12 + --WR $AAA:AA $555:55 $AAA:AA + dict.snes("SNES_SET_BANK", 0x00) + + --put cart in program mode + dict.pinport("CTL_SET_LO", "SNES_RST") + + dict.snes("SNES_ROM_WR", 0x0AAA, 0xAA) + dict.snes("SNES_ROM_WR", 0x0555, 0x55) + dict.snes("SNES_ROM_WR", 0x0AAA, 0x90) + --read manf ID + rv = dict.snes("SNES_ROM_RD", 0x0000) + if debug then print("attempted read SNES ROM manf ID:", string.format("%X", rv)) end +-- if ( rv[RV_DATA0_IDX] != SST_MANF_ID ) { +-- return GEN_FAIL; +-- //no need for software exit since failed to enter +-- } +-- + --read prod ID + rv = dict.snes("SNES_ROM_RD", 0x0002) + if debug then print("attempted read SNES ROM prod ID:", string.format("%X", rv)) end + rv = dict.snes("SNES_ROM_RD", 0x001C) + if debug then print("attempted read SNES density ID: ", string.format("%X", rv)) end + rv = dict.snes("SNES_ROM_RD", 0x001E) + if debug then print("attempted read SNES boot sect ID:", string.format("%X", rv)) end +-- if ( (rv[RV_DATA0_IDX] == SST_PROD_128) +-- || (rv[RV_DATA0_IDX] == SST_PROD_256) +-- || (rv[RV_DATA0_IDX] == SST_PROD_512) ) { +-- //found expected manf and prod ID +-- flash->manf = SST_MANF_ID; +-- flash->part = rv[RV_DATA0_IDX]; +-- flash->wr_dict = DICT_NES; +-- flash->wr_opcode = NES_PPU_WR; +-- } +-- + --exit software +-- dict.nes("NES_PPU_WR", 0x0000, 0xF0) + dict.snes("SNES_ROM_WR", 0x0000, 0xF0) +-- + + --exit program mode + dict.pinport("CTL_SET_HI", "SNES_RST") + + --return true + +end + +-- global variables so other modules can use them + + +-- call functions desired to run when script is called/imported + + +-- functions other modules are able to call +snes.read_reset_vector = read_reset_vector +snes.read_flashID = read_flashID + +-- return the module's table +return snes + diff --git a/shared/shared_dict_buffer.h b/shared/shared_dict_buffer.h index 70c1fa8..b1b65f1 100644 --- a/shared/shared_dict_buffer.h +++ b/shared/shared_dict_buffer.h @@ -155,6 +155,10 @@ //operand LSB mapper variant #define NOVAR 0 + + #define LOROM 0 + #define HIROM 1 + //set function //miscdata: buffer number //operMSB: (might be needed if this is a ponter..?) or might need more than one function def.. diff --git a/shared/shared_dict_pinport.h b/shared/shared_dict_pinport.h index 4d9e9a4..2dbe123 100644 --- a/shared/shared_dict_pinport.h +++ b/shared/shared_dict_pinport.h @@ -78,6 +78,7 @@ // PC8 "EXP0" NES EXP0, cart-console /RESET #define C8_ 8 #define EXP0_ 8 + #define SNES_RST_ 8 // PC9 "LED" kazzos tied this to NES EXP9, INL6 connects to CIC CLK #define C9_ 9 #define LED_ 9 @@ -169,5 +170,13 @@ #define EXP_DISABLE_ 19 #define EXP_SET_ 20 +//============================ +//HIGH ADDR PORT 8bit WIDE ACCESS +//opcode: type of operation +//operand: value to place on bus +//============================ +#define HADDR_ENABLE_ 21 +#define HADDR_DISABLE_ 22 +#define HADDR_SET_ 23 #endif diff --git a/shared/shared_dict_snes.h b/shared/shared_dict_snes.h index 5c4e510..e74b47c 100644 --- a/shared/shared_dict_snes.h +++ b/shared/shared_dict_snes.h @@ -17,80 +17,18 @@ //============================================================================================= -// OPCODES with no operand and no return value besides SUCCESS/ERROR_CODE +//set A16-23 aka bank number +#define SNES_SET_BANK 0x00 +//read from current bank at provided address +//SNES reset is unaffected +#define SNES_ROM_RD 0x01 //RL=3 +//write from current bank at provided address +//SNES reset is unaffected +#define SNES_ROM_WR 0x02 -//============================================================================================= -// OPCODES WITH OPERAND and no return value besides SUCCESS/ERROR_CODE -//============================================================================================= -// Detect this opcode/operand setup with opcode between the following defines: -#define SNES_OPCODE_24BOP_MIN 0x00 -#define SNES_OPCODE_24BOP_MAX 0x7F -// -//============================================================================================= -//============================================================================================= - - -//SNES has 24bit address bus which is max operands we can send -//So have to get a little creative for writing to 24bit address -//prob easiest to just keep last latched value of A23-16 - -//write to SNES in program mode (/RESET low) -//only can send A15-A0 -//A23-16 are last latched values -//SNES /ROMSEL always goes low -//program writes don't allow access to SRAM -//program access aligns ROM linearly and is independent of Hi/Lo switch -#define SNES_A15_A0_PRGM_WR 0x00 - -//write to SNES in play mode (/RESET high) -//only can send A15-A0 -//A23-16 are last latched values -//SNES /ROMSEL always goes low -//play writes allow access to SRAM -//program access aligns ROM based on position of Hi/Lo switch -#define SNES_A15_A0_PLAY_WR 0x01 - -//write to SNES in play mode (/RESET high) -//only can send A15-A0 -//A23-16 are last latched values -//SNES /ROMSEL not affected -//play writes allow access to SRAM -//program access aligns ROM based on position of Hi/Lo switch -#define SNES_A15_A0_NO_ROMSEL_PLAY_WR 0x02 - -//============================================================================================= -// OPCODES WITH OPERAND 8bit RETURN VALUE plus SUCCESS/ERROR_CODE -//============================================================================================= -// Detect this opcode/operand setup with opcode between the following defines: -#define SNES_OPCODE_24BOP_8BRV_MIN 0x80 -#define SNES_OPCODE_24BOP_8BRV_MAX 0xFF -// -//============================================================================================= -//============================================================================================= - -//can supply entire 24bit address - -//write to SNES in program mode (/RESET low) -//SNES /ROMSEL always goes low -//program reads don't allow access to SRAM -//program access aligns ROM linearly and is independent of Hi/Lo switch -#define SNES_PRGM_RD 0x80 - -//write to SNES in play mode (/RESET high) -//SNES /ROMSEL always goes low -//play reads allow access to SRAM -//program access aligns ROM based on position of Hi/Lo switch -#define SNES_PLAY_RD 0x81 - -//write to SNES in play mode (/RESET high) -//SNES /ROMSEL not active -//play reads allow access to SRAM -//program access aligns ROM based on position of Hi/Lo switch -#define SNES_NO_ROMSEL_PLAY_RD 0x82 - #endif diff --git a/shared/shared_dictionaries.h b/shared/shared_dictionaries.h index 6123bbd..52ed407 100644 --- a/shared/shared_dictionaries.h +++ b/shared/shared_dictionaries.h @@ -65,8 +65,8 @@ //============================================================================================= //============================================================================================= -//#define DICT_SNES 4 -//#include "shared_dict_snes.h" +#define DICT_SNES 4 +#include "shared_dict_snes.h" //snes dictionary contains commands //These commands rely on io initialization from io dictionary prior to calling //This library is intended to contain all SNES related opcodes/commands diff --git a/shared/shared_errors.h b/shared/shared_errors.h index c27deed..7c5ef29 100644 --- a/shared/shared_errors.h +++ b/shared/shared_errors.h @@ -18,8 +18,8 @@ // ////reserved libusb erro 165 // -//#define ERR_UNKN_SNES_OPCODE 170 -// +#define ERR_UNKN_SNES_OPCODE 170 + #define ERR_UNKN_BUFF_OPCODE 180 #define ERR_BUFN_DOES_NOT_EXIST 181