333 lines
11 KiB
C
333 lines
11 KiB
C
#include "nes.h"
|
|
|
|
/* Desc:check if PPU /A13 -> CIRAM /CE jumper present
|
|
* Does NOT check if PPU A13 is inverted and then drives CIRAM /CE
|
|
* Pre: nes_init() been called to setup i/o
|
|
* Post:PPU /A13 left high (disabled), all other ADDRH signals low
|
|
* Rtn: FALSE if jumper is not set
|
|
*/
|
|
int jumper_ciramce_ppuA13n( USBtransfer *transfer )
|
|
{
|
|
uint8_t rv[RV_DATA0_IDX+1];
|
|
|
|
//check that we can clear CIRAM /CE with PPU /A13
|
|
dictionary_call( transfer, DICT_PINPORT, ADDRH_SET, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//read CIRAM /CE pin
|
|
dictionary_call( transfer, DICT_PINPORT, CICE_RD, 0, 0,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
|
|
//CIRAM /CE's port PIN register contents are now in rv[RV_DATA_IDX]
|
|
//need to mask out the CIRAM /CE pin
|
|
if ( rv[RV_DATA0_IDX] & CICE_MSK ) {
|
|
//CIRAM /CE pin was always high regardless of PPU /A13
|
|
debug("CIRAM /CE high when /A13 low ");
|
|
return FALSE;
|
|
}
|
|
|
|
//set PPU /A13 high
|
|
dictionary_call( transfer, DICT_PINPORT, ADDRH_SET, PPU_A13N_MSK, 0,
|
|
USB_IN, NULL, 1);
|
|
//read CIRAM /CE pin
|
|
dictionary_call( transfer, DICT_PINPORT, CICE_RD, 0, 0,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
|
|
//CIRAM /CE's port PIN register contents are now in rv[RV_DATA_IDX]
|
|
//need to mask out the CIRAM /CE pin
|
|
if ( (rv[RV_DATA0_IDX] & CICE_MSK) == 0 ) {
|
|
//CICE jumper not present
|
|
debug("CIRAM /CE low when /A13 high ");
|
|
return FALSE;
|
|
}
|
|
|
|
//CICE low jumper appears to be present
|
|
debug("CIRAM /CE <- PPU /A13 jumper present");
|
|
return ~FALSE;
|
|
|
|
}
|
|
|
|
/* Desc:check if PPU A13 is inverted then drives CIRAM /CE
|
|
* Some mappers may do this including INLXO-ROM boards
|
|
* Does NOT check if PPU /A13 is drives CIRAM /CE
|
|
* Pre: nes_init() been called to setup i/o
|
|
* Post:PPU A13 left disabled (hi)
|
|
* Rtn: FALSE if inverted PPU A13 doesn't drive CIRAM /CE
|
|
*/
|
|
int ciramce_inv_ppuA13( USBtransfer *transfer )
|
|
{
|
|
uint8_t rv[RV_DATA0_IDX+1];
|
|
|
|
//set PPU /A13 low
|
|
dictionary_call( transfer, DICT_PINPORT, ADDRH_SET, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//read CIRAM /CE pin
|
|
dictionary_call( transfer, DICT_PINPORT, CICE_RD, 0, 0,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
|
|
// CIRAM /CE should be high if inverted A13 is what drives it
|
|
if ( (rv[RV_DATA0_IDX] & CICE_MSK) == 0 ) {
|
|
//CICE jumper not present
|
|
debug("CIRAM /CE low when /A13 low ");
|
|
return FALSE;
|
|
}
|
|
|
|
//check that we can clear CIRAM /CE with PPU /A13 high
|
|
dictionary_call( transfer, DICT_PINPORT, ADDRH_SET, PPU_A13_MSK, 0,
|
|
USB_IN, NULL, 1);
|
|
//read CIRAM /CE pin
|
|
dictionary_call( transfer, DICT_PINPORT, CICE_RD, 0, 0,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
|
|
// CIRAM /CE should be low if inverted A13 is what drives it
|
|
if ( rv[RV_DATA0_IDX] & CICE_MSK ) {
|
|
//CIRAM /CE pin was always high regardless of PPU /A13
|
|
debug("CIRAM /CE high when /A13 high ");
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//CICE low jumper appears to be present
|
|
debug("CIRAM /CE <- inverse PPU /A13");
|
|
return ~FALSE;
|
|
|
|
}
|
|
|
|
|
|
/* Desc:check for famicom audio in->out jumper
|
|
* This drives EXP6 (RF out) -> EXP0 (APU in) which is backwards..
|
|
* not much can do about that for current kazzo designs
|
|
* There are probably caps/resistors for synth carts anyway
|
|
* but to be safe only apply short pulses.
|
|
* While we typically don't want to apply 5v to EXP port on NES carts,
|
|
* this only does so for EXP6 which is safe on current designs.
|
|
* All other EXP1-8 pins are only driven low.
|
|
* Pre: nes_init() been called to setup i/o
|
|
* which makes EXP0 floating i/p
|
|
* Post:EXP FF left disabled and EXP0 floating
|
|
* AXLOE pin returned to input with pullup
|
|
* Rtn: FALSE if jumper/connection is not present
|
|
* Test:Works on non-expansion sound carts obviously
|
|
* Works on VRC6 and VRC7
|
|
* Others untested
|
|
*/
|
|
int famicom_sound( USBtransfer *transfer )
|
|
{
|
|
uint8_t rv[RV_DATA0_IDX+1];
|
|
|
|
//EXP0 should be floating input
|
|
//AXLOE pin needs to be set as output and
|
|
//EXP FF needs enabled before we can clock it,
|
|
//but don't leave it enabled before exiting function
|
|
|
|
//set AXLOE to output
|
|
dictionary_call( transfer, DICT_PINPORT, AXLOE_OP, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//enable EXP FF
|
|
dictionary_call( transfer, DICT_PINPORT, EXPFF_OP, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//Latch low first
|
|
dictionary_call( transfer, DICT_PINPORT, ADDRX_SET, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//read EXP0 Famicom APU audio pin
|
|
dictionary_call( transfer, DICT_PINPORT, FC_APU_RD, 0, 0,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
|
|
//need to mask out the pin
|
|
if ( rv[RV_DATA0_IDX] & FC_APU_MSK ) {
|
|
debug("RF audio out (EXP6) didn't drive APU audio in (EXP0) low");
|
|
//disable EXP FF
|
|
dictionary_call( transfer, DICT_PINPORT, EXPFF_FLT, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//retun AXLOE to input
|
|
dictionary_call( transfer, DICT_PINPORT, AXLOE_IP, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
return FALSE;
|
|
}
|
|
|
|
//Latch pin high
|
|
dictionary_call( transfer, DICT_PINPORT, ADDRX_SET, FC_RF_MSK, 0,
|
|
USB_IN, NULL, 1);
|
|
//read EXP0 Famicom APU audio pin
|
|
dictionary_call( transfer, DICT_PINPORT, FC_APU_RD, 0, 0,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
//disable EXP FF
|
|
dictionary_call( transfer, DICT_PINPORT, EXPFF_FLT, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
//retun AXLOE to input
|
|
dictionary_call( transfer, DICT_PINPORT, AXLOE_IP, 0, 0,
|
|
USB_IN, NULL, 1);
|
|
|
|
//mask pin from byte
|
|
if ( (rv[RV_DATA0_IDX] & FC_APU_MSK) == 0 ) {
|
|
debug("RF audio out (EXP6) didn't drive APU audio in (EXP0) high");
|
|
return FALSE;
|
|
}
|
|
|
|
//CICE low jumper appears to be present
|
|
debug("RF audio out (EXP6) is connected to APU audio in (EXP0)");
|
|
return ~FALSE;
|
|
|
|
}
|
|
|
|
/* Desc:PRG-ROM flash manf/prod ID sense test
|
|
* Using EXP0 /WE writes
|
|
* Only senses SST flash ID's
|
|
* Assumes that isn't getting tricked by having manf/prodID at $8000/8001
|
|
* could add check and increment read address to ensure doesn't get tricked..
|
|
* Pre: nes_init() been called to setup i/o
|
|
* exp0 pullup test must pass
|
|
* if ROM A14 is mapper controlled it must be low when CPU A14 is low
|
|
* controlling A14 outside of this function acts as a means of bank size detection
|
|
* Post:memory manf/prod ID set to read values if passed
|
|
* Software mode exited if entered successfully
|
|
* Rtn: SUCCESS if flash sensed, GEN_FAIL if not, neg if error
|
|
*/
|
|
int read_flashID_prgrom_exp0( USBtransfer *transfer, memory *flash ) {
|
|
|
|
uint8_t rv[RV_DATA0_IDX];
|
|
|
|
//enter software mode
|
|
//ROMSEL controls PRG-ROM /OE which needs to be low for flash writes
|
|
//So unlock commands need to be addressed below $8000
|
|
//DISCRETE_EXP0_PRGROM_WR doesn't toggle /ROMSEL by definition though, so A15 is unused
|
|
// 15 14 13 12
|
|
// 0x5 = 0b 0 1 0 1 -> $5555
|
|
// 0x2 = 0b 0 0 1 0 -> $2AAA
|
|
dictionary_call( transfer, DICT_NES, DISCRETE_EXP0_PRGROM_WR, 0x5555, 0xAA,
|
|
USB_IN, NULL, 1);
|
|
dictionary_call( transfer, DICT_NES, DISCRETE_EXP0_PRGROM_WR, 0x2AAA, 0x55,
|
|
USB_IN, NULL, 1);
|
|
dictionary_call( transfer, DICT_NES, DISCRETE_EXP0_PRGROM_WR, 0x5555, 0x90,
|
|
USB_IN, NULL, 1);
|
|
//read manf ID
|
|
dictionary_call( transfer, DICT_NES, NES_CPU_RD, 0x8000, NILL,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
debug("manf id: %x", rv[RV_DATA0_IDX]);
|
|
if ( rv[RV_DATA0_IDX] != SST_MANF_ID ) {
|
|
return GEN_FAIL;
|
|
//no need for software exit since failed to enter
|
|
}
|
|
|
|
//read prod ID
|
|
dictionary_call( transfer, DICT_NES, NES_CPU_RD, 0x8001, NILL,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
debug("prod id: %x", rv[RV_DATA0_IDX]);
|
|
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];
|
|
}
|
|
|
|
//exit software
|
|
dictionary_call( transfer, DICT_NES, DISCRETE_EXP0_PRGROM_WR, 0x8000, 0xF0,
|
|
USB_IN, NULL, 1);
|
|
|
|
//verify exited
|
|
//dictionary_call( transfer, DICT_NES, NES_CPU_RD, 0x8000, NILL,
|
|
// USB_IN, rv, RV_DATA0_IDX+1);
|
|
//debug("prod id: %x", rv[RV_DATA0_IDX]);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/* Desc:CHR-ROM flash manf/prod ID sense test
|
|
* Only senses SST flash ID's
|
|
* Does not make CHR bank writes so A14-A13 must be made valid outside of this funciton
|
|
* An NROM board does this by tieing A14:13 to A12:11
|
|
* Other mappers will pass this function if PT0 has A14:13=01, PT1 has A14:13=10
|
|
* Assumes that isn't getting tricked by having manf/prodID at $0000/0001
|
|
* could add check and increment read address to ensure doesn't get tricked..
|
|
* Pre: nes_init() been called to setup i/o
|
|
* Post:memory manf/prod ID set to read values if passed
|
|
* Software mode exited if entered successfully
|
|
* Rtn: SUCCESS if flash sensed, GEN_FAIL if not, neg if error
|
|
*/
|
|
int read_flashID_chrrom_8K( USBtransfer *transfer, memory *flash ) {
|
|
|
|
uint8_t rv[RV_DATA0_IDX];
|
|
|
|
//enter software mode
|
|
//NROM has A13 tied to A11, and A14 tied to A12.
|
|
//So only A0-12 needs to be valid
|
|
//A13 needs to be low to address CHR-ROM
|
|
// 15 14 13 12
|
|
// 0x5 = 0b 0 1 0 1 -> $1555
|
|
// 0x2 = 0b 0 0 1 0 -> $0AAA
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_WR, 0x1555, 0xAA,
|
|
USB_IN, NULL, 1);
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_WR, 0x0AAA, 0x55,
|
|
USB_IN, NULL, 1);
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_WR, 0x1555, 0x90,
|
|
USB_IN, NULL, 1);
|
|
//read manf ID
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_RD, 0x0000, NILL,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
debug("manf id: %x", rv[RV_DATA0_IDX]);
|
|
if ( rv[RV_DATA0_IDX] != SST_MANF_ID ) {
|
|
return GEN_FAIL;
|
|
//no need for software exit since failed to enter
|
|
}
|
|
|
|
//read prod ID
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_RD, 0x0001, NILL,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
debug("prod id: %x", rv[RV_DATA0_IDX]);
|
|
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];
|
|
}
|
|
|
|
//exit software
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_WR, 0x0000, 0xF0,
|
|
USB_IN, NULL, 1);
|
|
|
|
//verify exited
|
|
//dictionary_call( transfer, DICT_NES, NES_PPU_RD, 0x0000, NILL,
|
|
// USB_IN, rv, RV_DATA0_IDX+1);
|
|
//debug("prod id: %x", rv[RV_DATA0_IDX]);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* Desc:Simple CHR-RAM sense test
|
|
* A more thourough test should be implemented in firmware
|
|
* This one simply tests one address in PPU address space
|
|
* Pre: nes_init() been called to setup i/o
|
|
* Post:
|
|
* Rtn: SUCCESS if ram sensed, GEN_FAIL if not, neg if error
|
|
*/
|
|
int ppu_ram_sense( USBtransfer *transfer, uint16_t addr ) {
|
|
|
|
uint8_t rv[RV_DATA0_IDX];
|
|
|
|
//write 0xAA to addr
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_WR, addr, 0xAA,
|
|
USB_IN, NULL, 1);
|
|
//try to read it back
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_RD, addr, NILL,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
debug("reading back 0xAA: %x", rv[RV_DATA0_IDX]);
|
|
if ( rv[RV_DATA0_IDX] != 0xAA ) {
|
|
return GEN_FAIL;
|
|
}
|
|
|
|
//write 0x55 to addr
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_WR, addr, 0x55,
|
|
USB_IN, NULL, 1);
|
|
//try to read it back
|
|
dictionary_call( transfer, DICT_NES, NES_PPU_RD, addr, NILL,
|
|
USB_IN, rv, RV_DATA0_IDX+1);
|
|
debug("reading back 0x55: %x", rv[RV_DATA0_IDX]);
|
|
if ( rv[RV_DATA0_IDX] != 0x55 ) {
|
|
return GEN_FAIL;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|