#include "nes.h" //only need this file if connector is present on the device #ifdef NES_CONN //================================================================================================= // // NES operations // This file includes all the nes functions possible to be called from the nes dictionary. // // See description of the commands contained here in shared/shared_dictionaries.h // //================================================================================================= //global variables uint8_t cur_bank; //used by some flash algos, must be initialized prior to depending on it uint16_t bank_table; //address offset of bank table for mapper writes with bus conflicts /* Desc:Function takes an opcode which was transmitted via USB * then decodes it to call designated function. * shared_dict_nes.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_nes.h * Post:function call complete. * Rtn: SUCCESS if opcode found and completed, error if opcode not present or other problem. */ uint8_t nes_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 DISCRETE_EXP0_PRGROM_WR: discrete_exp0_prgrom_wr( operand, miscdata ); break; case DISC_PUSH_EXP0_PRGROM_WR: disc_push_exp0_prgrom_wr( operand, miscdata ); break; case NES_PPU_WR: nes_ppu_wr( operand, miscdata ); break; case NES_CPU_WR: nes_cpu_wr( operand, miscdata ); break; case NES_M2_LOW_WR: nes_m2_low_wr( operand, miscdata ); break; case NES_DUALPORT_WR: nes_dualport_wr( operand, miscdata ); break; // case DISCRETE_EXP0_MAPPER_WR: // discrete_exp0_mapper_wr( operand, miscdata ); // break; case NES_MMC1_WR: mmc1_wr( operand, miscdata, 0 ); break; case SET_CUR_BANK: cur_bank = operand; break; case SET_BANK_TABLE: bank_table = operand; break; case NROM_PRG_FLASH_WR: nrom_prgrom_flash_wr( operand, miscdata ); break; case NROM_CHR_FLASH_WR: nrom_chrrom_flash_wr( operand, miscdata ); break; case MMC1_PRG_FLASH_WR: mmc1_prgrom_flash_wr( operand, miscdata ); break; case MMC1_CHR_FLASH_WR: mmc1_chrrom_flash_wr( operand, miscdata ); break; case UNROM_PRG_FLASH_WR: unrom_prgrom_flash_wr( operand, miscdata ); break; case CNROM_CHR_FLASH_WR: cnrom_chrrom_flash_wr( operand, miscdata ); break; case MMC3_PRG_FLASH_WR: mmc3_prgrom_flash_wr( operand, miscdata ); break; case MMC3_CHR_FLASH_WR: mmc3_chrrom_flash_wr( operand, miscdata ); break; case MMC4_PRG_SOP_FLASH_WR: mmc4_prgrom_sop_flash_wr( operand, miscdata ); break; case MMC4_CHR_FLASH_WR: mmc4_chrrom_flash_wr( operand, miscdata ); break; case CDREAM_CHR_FLASH_WR: cdream_chrrom_flash_wr( operand, miscdata ); break; //8bit return values: case EMULATE_NES_CPU_RD: rdata[RD_LEN] = BYTE_LEN; rdata[RD0] = emulate_nes_cpu_rd( operand ); break; case NES_CPU_RD: rdata[RD_LEN] = BYTE_LEN; rdata[RD0] = nes_cpu_rd( operand ); break; case NES_PPU_RD: rdata[RD_LEN] = BYTE_LEN; rdata[RD0] = nes_ppu_rd( operand ); break; case NES_DUALPORT_RD: rdata[RD_LEN] = BYTE_LEN; rdata[RD0] = nes_dualport_rd( operand ); break; // case CIRAM_A10_MIRROR: // rdata[RD_LEN] = BYTE_LEN; // rdata[RD0] = ciram_a10_mirroring( ); // break; case GET_CUR_BANK: rdata[RD_LEN] = BYTE_LEN; rdata[RD0] = cur_bank; break; case GET_BANK_TABLE: rdata[RD_LEN] = HWORD_LEN; rdata[RD0] = bank_table; rdata[RD1] = bank_table>>8; break; default: //macro doesn't exist return ERR_UNKN_NES_OPCODE; } return SUCCESS; } /* 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(); } //like above, but push on EXP0 instead of pullup void disc_push_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 EXP0_HI(); //Twp = 40ns, Tds = 40ns, Tdh = 0ns //16Mhz avr clk = 62.5ns period guarantees timing reqts DATA_IP(); } /* Desc: Discrete board MAPPER write without bus conflicts * will also write to PRG-ROM, but PRG-ROM shouldn't output * data while writting to mapper. Thus removing need for bank table. * NOTE: I think it would be possible to write one value to mapper * and another value to PRG-ROM. * 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 * Note:addrH bit7 has no effect (ends up on PPU /A13) * M2 signal untouched * Pre: nes_init() setup of io pins * Post:data latched by MAPPER, will also be written to PRG-ROM afterwards * address left on bus * data left on bus, but pullup only * EXP0 left pulled up * Rtn: None */ //void discrete_exp0_mapper_wr( uint16_t addr, uint8_t data ) //{ // //Float EXP0 as it should be in NES // EXP0_IP_FL(); // //EXP0_OP(); //tas = 0ns, tah = 30ns // //EXP0_LO(); // // //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 // M2_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 // M2_LO(); // ROMSEL_HI(); // // //retore PRG R/W to default // PRGRW_HI(); // // EXP0_IP_PU(); //Twp = 40ns, Tds = 40ns, Tdh = 0ns // //Free data bus // DATA_IP(); // // return; // // /* // ADDR_SET(addr); // // DATA_OP(); // DATA_SET(data); // // //start write to PRG-ROM (latch address) // exp0_op(); //tas = 0ns, tah = 30ns // exp0_lo(); // // //enable write to mapper PRG R/W LO // PRGRW_LO(); // ROMSEL_LO(); //fact that it's low for such a short time might also if PRG-ROM does output data // // NOP(); //AVR didn't need this delay // NOP(); //AVR didn't need this delay // NOP(); //AVR didn't need this delay // NOP(); //AVR didn't need this delay // NOP(); //AVR didn't need this delay // NOP(); //AVR didn't need this delay // //clock mapper register, should not enable PRG-ROM output since /WE low // NOP(); //AVR didn't need this delay // NOP(); //AVR didn't need this delay // ROMSEL_HI(); //data latched on rising edge // // //Could output other data here that would like to be written to PRG-ROM // //I'm not certain an actual write gets applied to PRG-ROM as /OE is supposed to be high whole time.. // // NOP(); //AVR didn't need this delay // //return to default // PRGRW_HI(); // // 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( uint16_t addr ) { 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_IP_FL(); //this could have been left pulled up M2_LO(); //start of CPU cycle ROMSEL_HI(); //trails M2 PRGRW_HI(); //happens just after M2 //set address bus ADDR_SET(addr); //couple NOP's to wait a bit NOP(); NOP(); //set M2 and /ROMSEL if( addr >= 0x8000 ) { //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 DATA_RD(read); //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 M2_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 M2_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 M2_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 M2_LO(); ROMSEL_HI(); //retore PRG R/W to default PRGRW_HI(); //Free data bus DATA_IP(); } /* Desc:NES CPU Write, but M2 remains low * Allows writes to flash, but not memory if M2 must be high for mapper to latch the write * A15 decoded to enable /ROMSEL * Note:addrH bit7 has no effect (ends up on PPU /A13) * EXP0 as-is * 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_m2_low_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 // M2_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 // M2_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:NES dual port Read from the PPU * /A13 as ignored * 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_dualport_rd( uint16_t addr ) { uint8_t read; //return value ADDR_SET( addr ); //enable data path M2_HI(); //M2 is kinda like R/W setting direction ROMSEL_LO(); //enable data buffers //data should now be driven on the bus but invalid //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 //latch data DATA_RD(read); //return bus to default CSRD_HI(); M2_LO(); ROMSEL_HI(); return read; } /* Desc:NES DUALPORT Write * /A13 ignored * Pre: nes_init() setup of io pins * Post:data written to addrHL * address left on bus * data bus left clear * Rtn: None */ void nes_dualport_wr( uint16_t addr, uint8_t data ) { ADDR_SET( addr ); //enable data path M2_LO(); //M2 is kinda like R/W setting direction ROMSEL_LO(); //enable data buffers //data should now be driven on the bus but invalid //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(); ROMSEL_HI(); } /* 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 A11, clear A10 // //ADDRH(A11_BYTE); setting A11 in this manner doesn't work for some reason.. // ADDR_SET(0x0800); // //CIA10_RD(readH); // readH = (C11bank->IDR & (1<IDR & (1< 1 screen A // if ((readV==0) && (readH==0)) return MIR_1SCNA; // //if CIRAM A10 was always high -> 1 screen 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 M2_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 //extra NOP was needed on stm6 as address hadn't settled in time for the very first read NOP(); //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 M2_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 NOP(); //adding extra NOP as it was needed on PRG //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; } /* Desc:NES DUAL PORT PPU Page Read with optional USB polling * /A13 ignored * 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_dualport_page_rd_poll( uint8_t *data, uint8_t addrH, uint8_t first, uint8_t len, uint8_t poll ) { uint8_t i; //ignore /A13, board doesn't see it anyway ADDRH(addrH); //now that data bus is no longer needed, //can enable data path out of cart M2_HI(); ROMSEL_LO(); //set CHR /RD and /WR CSRD_LO(); //set lower address bits ADDRL(first); //doing this prior to entry and right after latching NOP(); //adding extra NOP as it was needed on PRG //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(); M2_LO(); ROMSEL_HI(); //return index of last byte read return i; } /* Desc:NES MMC1 Mapper Register Write * write to entirety of MMC1 register * address selects register that's written to * address must be >= $8000 where registers are located * Pre: nes_init() setup of io pins * MMC1 shift register has been reset by writting with D7 set * bit7 must be clear, else the shift register will be reset * Post:MMC1 register contains value provided * address left on bus * data left on bus, but pullup only * Rtn: None */ void mmc1_wr( uint16_t addr, uint8_t data, uint8_t reset ) { uint8_t i; //reset shift register if requested if( reset ) { nes_cpu_rd(0x8000); nes_cpu_wr(0x8000, 0x80); } //5 bits in register D0-4, so 5 total writes through D0 for( i=0; i<5; i++) { //MMC1 ignores all but the first write, so perform a read first nes_cpu_rd(addr); nes_cpu_wr(addr, data); data = data >> 1; } return; } /* Desc:NES NROM PRG-ROM FLASH Write * Also used for discrete mappers with 32KB banking (CNROM, BxROM, etc) * Pre: nes_init() setup of io pins * Post:Byte written and ready for another write * Rtn: None */ void nrom_prgrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //unlock and write data discrete_exp0_prgrom_wr(0x5555, 0xAA); discrete_exp0_prgrom_wr(0x2AAA, 0x55); discrete_exp0_prgrom_wr(0x5555, 0xA0); discrete_exp0_prgrom_wr(addr, data); do { rv = nes_cpu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_cpu_rd(addr)); //TODO handle timeout return; } /* Desc:NES NROM CHR-ROM FLASH Write * Pre: nes_init() setup of io pins * Post:Byte written and ready for another write * Rtn: None */ void nrom_chrrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //unlock and write data nes_ppu_wr(0x1555, 0xAA); nes_ppu_wr(0x0AAA, 0x55); nes_ppu_wr(0x1555, 0xA0); nes_ppu_wr(addr, data); do { rv = nes_ppu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_ppu_rd(addr)); //TODO handle timeout return; } /* Desc:NES MMC1 PRG-ROM FLASH Write * Pre: nes_init() setup of io pins * MMC1 must be properly inialized for flashing * 32KB mode with current bank selected * addr must be between $8000-FFFF as prescribed by init * Post:Byte written and ready for another write * Rtn: None */ void mmc1_prgrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //make a generic write to mapper reg so the last write will block all subsequent writes mmc1_wr(0xC000, 0x05, 0); //just write to random CHR ROM register //unlock and write data //all these writes will be block by MMC1 mapper register due to valid write above that ends with a write nes_cpu_wr(0x5555, 0xAA); nes_cpu_wr(0xAAAA, 0x55); nes_cpu_wr(0x5555, 0xA0); nes_cpu_wr(addr, data); do { rv = nes_cpu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_cpu_rd(addr)); //TODO handle timeout return; } /* Desc:NES MMC1 CHR-ROM FLASH Write * Pre: nes_init() setup of io pins * cur_bank global var must be set to desired mapper register value * Post:Byte written and ready for another write * Rtn: None */ void mmc1_chrrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //set banks for unlock commands mmc1_wr(0xA000, 0x02, 0); //PT1 always set to 0x05 for $5555 command //send unlock command nes_ppu_wr(0x1555, 0xAA); nes_ppu_wr(0x0AAA, 0x55); nes_ppu_wr(0x1555, 0xA0); //select desired bank for write mmc1_wr(0xA000, cur_bank, 0); //write the data nes_ppu_wr(addr, data); do { rv = nes_ppu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_ppu_rd(addr)); return; } /* Desc:NES UNROM PRG-ROM FLASH Write * Pre: nes_init() setup of io pins * cur_bank global var must be set to desired mapper register value * bank_table global var must be set to base address of the bank table * Post:Byte written and ready for another write * Rtn: None */ void unrom_prgrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //set A14 low for lower bank so to satisfy unlock commands nes_cpu_wr(bank_table, 0x00); //unlock the flash discrete_exp0_prgrom_wr(0x5555, 0xAA); discrete_exp0_prgrom_wr(0x2AAA, 0x55); discrete_exp0_prgrom_wr(0x5555, 0xA0); //select desired bank and write data nes_cpu_wr(bank_table+cur_bank, cur_bank); discrete_exp0_prgrom_wr(addr, data); do { rv = nes_cpu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_cpu_rd(addr)); return; } /* Desc:NES CNROM CHR-ROM FLASH Write * Pre: nes_init() setup of io pins * cur_bank global var must be set to desired mapper register value * bank_table global var must be set to base address of the bank table * Post:Byte written and ready for another write * Rtn: None */ void cnrom_chrrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //unlock the flash nes_cpu_wr(bank_table+2, 0x02); nes_ppu_wr(0x1555, 0xAA); nes_cpu_wr(bank_table+1, 0x01); nes_ppu_wr(0x0AAA, 0x55); nes_cpu_wr(bank_table+2, 0x02); nes_ppu_wr(0x1555, 0xA0); //select desired bank for the write nes_cpu_wr(bank_table+cur_bank, cur_bank); //write the byte nes_ppu_wr(addr, data); do { rv = nes_ppu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_ppu_rd(addr)); //TODO handle timeout return; } /* Desc:NES MMC3 PRG-ROM FLASH Write * Pre: nes_init() setup of io pins * MMC3 must be properly inialized for flashing * addr must be between $8000-9FFF as prescribed by init * Post:Byte written and ready for another write * Rtn: None */ void mmc3_prgrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //unlock and write data nes_cpu_wr(0xD555, 0xAA); nes_cpu_wr(0xAAAA, 0x55); nes_cpu_wr(0xD555, 0xA0); nes_cpu_wr(addr, data); //reset $8000 bank select register to a CHR reg nes_cpu_wr(0x8000, 0x00); do { rv = nes_cpu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_cpu_rd(addr)); //TODO handle timeout return; } /* Desc:NES MMC3 CHR-ROM FLASH Write * Pre: nes_init() setup of io pins * MMC3 must be properly inialized for flashing * addr must be between $0000-0FFF as prescribed by init * Post:Byte written and ready for another write * Rtn: None */ void mmc3_chrrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //unlock and write data nes_ppu_wr(0x1555, 0xAA); nes_ppu_wr(0x1AAA, 0x55); nes_ppu_wr(0x1555, 0xA0); nes_ppu_wr(addr, data); do { rv = nes_ppu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_ppu_rd(addr)); //TODO handle timeout return; } /* Desc:NES MMC4 PRG-ROM FLASH Write * Pre: nes_init() setup of io pins * MMC4 must be properly inialized for flashing * addr must be between $8000-BFFF as prescribed by init * desired bank must already be selected * cur_bank must be set to desired bank for recovery * Post:Byte written and ready for another write * Rtn: None */ void mmc4_prgrom_sop_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //unlock and write data SOP-44 flash nes_cpu_wr(0xFAAA, 0xAA); nes_cpu_wr(0xF555, 0x55); nes_cpu_wr(0xFAAA, 0xA0); nes_cpu_wr(addr, data); //corrupts bank register if addr $A000-AFFF //recover bank register as data write would have corrupted nes_cpu_wr(0xA000, cur_bank); do { rv = nes_cpu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_cpu_rd(addr)); //TODO handle timeout return; } /* Desc:NES MMC4 CHR-ROM FLASH Write * Pre: nes_init() setup of io pins * cur_bank global var must be set to desired mapper register value * Post:Byte written and ready for another write * Rtn: None */ void mmc4_chrrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //--set bank for unlock command //dict.nes("NES_CPU_WR", 0xB000, 0x0A) --4KB @ PPU $0000 -> $2AAA cmd & writes //dict.nes("NES_CPU_WR", 0xC000, 0x0A) --4KB @ PPU $0000 // //--send unlock command //dict.nes("NES_PPU_WR", 0x1555, 0xAA) //dict.nes("NES_PPU_WR", 0x0AAA, 0x55) //dict.nes("NES_PPU_WR", 0x1555, 0xA0) // //--select desired bank //dict.nes("NES_CPU_WR", 0xB000, bank) --4KB @ PPU $0000 -> $2AAA cmd & writes //dict.nes("NES_CPU_WR", 0xC000, bank) --4KB @ PPU $0000 //--write data //dict.nes("NES_PPU_WR", addr, value) //set banks for unlock commands nes_cpu_wr(0xB000, 0x0A); nes_cpu_wr(0xC000, 0x0A); //PT1 always set to 0x05 for $5555 command //send unlock command nes_ppu_wr(0x1555, 0xAA); nes_ppu_wr(0x0AAA, 0x55); nes_ppu_wr(0x1555, 0xA0); //select desired bank for write nes_cpu_wr(0xB000, cur_bank); nes_cpu_wr(0xC000, cur_bank); //write the data nes_ppu_wr(addr, data); do { rv = nes_ppu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_ppu_rd(addr)); return; } /* Desc:NES ColorDreams CHR-ROM FLASH Write * Pre: nes_init() setup of io pins * cur_bank global var must be set to desired mapper register value * bank_table global var must be set to base address of the bank table * The first PRG-ROM bank must be selected and bank table present * Post:Byte written and ready for another write * Rtn: None */ void cdream_chrrom_flash_wr( uint16_t addr, uint8_t data ) { uint8_t rv; //the CHR-ROM bank is in mapper register bits 4-7 uint8_t mapper_val = cur_bank << 4; //unlock the flash nes_cpu_wr(bank_table+0x20, 0x20); nes_ppu_wr(0x1555, 0xAA); nes_cpu_wr(bank_table+0x10, 0x10); nes_ppu_wr(0x0AAA, 0x55); nes_cpu_wr(bank_table+0x20, 0x20); nes_ppu_wr(0x1555, 0xA0); //select desired bank for the write nes_cpu_wr(bank_table+mapper_val, mapper_val); //write the byte nes_ppu_wr(addr, data); do { rv = nes_ppu_rd(addr); usbPoll(); //orignal kazzo needs this frequently to slurp up incoming data } while (rv != nes_ppu_rd(addr)); //TODO handle timeout return; } #endif //NES_CONN