/* TACOSUI.C ver 0.1 original Pascal FELBER felber@di.epfl.ch port by ikeda */ /* Pinout of the famicom extension: PRT D0 counter cleaer PRT D1 counter clock PRT D2 shift register sirial input PRT D3 shift register mode select L:shift H:load PRT D4 MMC CLK,CHR(VRAM) R/W write L ,read H (32pin,47pin) PRT D5 CHR /OE (56pin) PRT D6 PRG /OE (44pin) PRT D7 R/W ? 74HC161 9pin (14pin) PRT /STRB shift register clock GND CHR /CE */ #include #include #include #include #include #include #include "colortex.h" int type; int verify; int wait_delay; int xx=16; int cc=8; void delay(int i) { while(i--); } #define DELAY delay(wait_delay) /* Change those defines to match your compiler */ #define OUTPORT(port, val) outp(port, val); DELAY #define INPORT(port) inp(port) #include "prt.h" #define PRG 0 #define CHR 1 /* #define IDLE OUTPORT(B_8255_PC, 0xFF) #define READ_PRG OUTPORT(B_8255_PC, 0xFF-(0x01<>8)&0xFF); \ OUTPORT(A_8255_PA, addr&0xFF); \ set val OUTPORT(B_8255_PB, val); \ CHR /WE L WRITING! OUTPORT(B_8255_PROG, CLR_PC(CWR)); \ CHR /WE H OUTPORT(B_8255_PROG, SET_PC(CWR)); \ OUTPORT(B_8255_PROG, 0x80|PA_in|PB_in|PCl_out|PCh_out); \ OUTPORT(B_8255_PC, 0xFF) #define write_prg(val, addr) \ clk L OUTPORT(B_8255_PC, 0xFF-(0x01<>8)&0xFF); \ OUTPORT(A_8255_PA, addr&0xFF); \ set val OUTPORT(A_8255_PC, val); \ R/W L OUTPORT(B_8255_PROG, CLR_PC(RW)); \ CLK? H OUTPORT(B_8255_PROG, SET_PC(CLK)); \ PRG /OE L OUTPORT(B_8255_PROG, CLR_PC(POE)); \ CLK? L OUTPORT(B_8255_PROG, CLR_PC(CLK)); \ PRG /OE H OUTPORT(B_8255_PROG, SET_PC(POE)); \ PROG /WE H OUTPORT(B_8255_PROG, SET_PC(RW)); \ OUTPORT(A_8255_PROG, 0x80|PA_out|PB_out|PCl_in|PCh_in); \ CLK H OUTPORT(B_8255_PROG, SET_PC(CLK)) */ #define READ_PRG OUTPORT(PRT_DATA,(INPORT(PRT_DATA)|DATA5));\ OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA6) #define READ_CHR OUTPORT(PRT_DATA,(INPORT(PRT_DATA)|DATA6));\ OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA5) #define IDLE OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA4|DATA5|DATA6|DATA7) #define REG_CLK OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA1);\ OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA1) #define RESET_ADR OUTPORT(PRT_CONTROL,INPORT(PRT_CONTROL)&~STROBE);\ OUTPORT(PRT_CONTROL,INPORT(PRT_CONTROL)|STROBE) #define INC_ADR OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA0);\ OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA0) #define MAXBANKS 0x100 void set_address(unsigned int address); unsigned char read_register(void); void write_register(unsigned char data); void write_prg(unsigned char val); void write_chr(unsigned char val); int test_vram(void); void usage(char *s) { colortex(33,44); printf("★ ファミコン吸い出しユーティリティー ★\n");printf("\x1b[m"); colortex(33,44); printf("★ Tacosui Ver 0.02 [1999.10.21] ★\n");printf("\x1b[m"); colortex(33,44); printf("★ mail to : killer@be.mbn.or.jp ★\n");printf("\x1b[m"); printf("\x1b[m"); printf("使い方:%s\n",s); printf("[-v][-b int][-nc int][-np int][-w int][-a|-sp file|-sc file]\n"); printf(" -v 二重読み込み\n"); printf(" -x プログラムロムを32kごとに読み込む\n"); printf(" -b 読み込むマッパー番号\n"); printf(" -nc キャラクターロムのバンク数\n"); printf(" -np プログラムロムのバンク数\n"); printf(" -w 吸い出しのウェイト数\n"); printf(" -a ロムの調査\n"); printf(" -sp プログラムロムを吸い出しセーブする\n"); printf(" -sc キャラクターロムを吸い出しセーブする\n"); printf(" -sn ヘッダー付きのNESファイルを出力しセーブする\n"); colortex(33,40); printf(" 吸い出せるNESマッパー番号 \n\x1b[m"); colortex(33,44); printf("[0,1,2,3,4,5,9,10,18,65,70,75,77,87,184]\x1b[m\n"); colortex(33,44); printf("[1016] (ファミコンジャンプ2) \x1b[m\n"); } void switch_init() { } void switch_bank(int bank, int mem) { switch(type){ case 1: /* set_address(0x8000); write_prg(0x80); */ if(mem == PRG) { /* write_prg(bank, 0xE000); write_prg(bank>>1, 0xE000); write_prg(bank>>2, 0xE000); write_prg(bank>>3, 0xE000); write_prg(bank>>4, 0xE000); */ set_address(0xE000); write_prg(bank); write_prg(bank>>1); write_prg(bank>>2); write_prg(bank>>3); write_prg(bank>>4); } else { /* write_prg(bank<<1, 0xA000); write_prg(bank, 0xA000); write_prg(bank>>1, 0xA000); write_prg(bank>>2, 0xA000); write_prg(bank>>3, 0xA000); */ set_address(0xA000); write_prg(bank<<1); write_prg(bank); write_prg(bank>>1); write_prg(bank>>2); write_prg(bank>>3); } break; case 2: if(mem == PRG) { /* write_prg(bank, 0x8000); */ set_address(0x8000); write_prg(bank); } break; case 3: if(mem == CHR) { /* write_prg(bank, 0x8000); */ set_address(0x8000); write_prg(bank); } break; case 4: if(mem == PRG) { /* write_prg(6, 0x8000); write_prg(bank<<1, 0x8001); write_prg(7, 0x8000); write_prg((bank<<1)+1, 0x8001); */ set_address(0x8000); write_prg(6); INC_ADR; write_prg(bank<<1); set_address(0x8000); write_prg(7); INC_ADR; write_prg((bank<<1)+1); } else { /* write_prg(0x00, 0x8000); write_prg(bank<<3, 0x8001); write_prg(0x01, 0x8000); write_prg((bank<<3)+2, 0x8001); write_prg(0x02, 0x8000); write_prg((bank<<3)+4, 0x8001); write_prg(0x03, 0x8000); write_prg((bank<<3)+5, 0x8001); write_prg(0x04, 0x8000); write_prg((bank<<3)+6, 0x8001); write_prg(0x05, 0x8000); write_prg((bank<<3)+7, 0x8001); */ set_address(0x8000); write_prg(0x00); INC_ADR; write_prg(bank<<3); set_address(0x8000); write_prg(0x01); INC_ADR; write_prg((bank<<3)+2); set_address(0x8000); write_prg(0x02); INC_ADR; write_prg((bank<<3)+4); set_address(0x8000); write_prg(0x03); INC_ADR; write_prg((bank<<3)+5); set_address(0x8000); write_prg(0x04); INC_ADR; write_prg((bank<<3)+6); set_address(0x8000); write_prg(0x05); INC_ADR; write_prg((bank<<3)+7); } break; case 5: if(mem == PRG) { set_address(0x5114); write_prg((bank<<1)+0+0x80); set_address(0x5115); write_prg((bank<<1)+1+0x80); /* 5114 5115 5116 */ } else { set_address(0x5127); write_prg(bank); } break; case 9: if(mem == PRG) { set_address(0xA000); write_prg(bank); } else { set_address(0xB000); write_prg((bank<<1)+0); set_address(0xD000); write_prg((bank<<1)+1); set_address(0xE000); write_prg((bank<<1)+2); } break; case 10: if(mem == PRG) { set_address(0xA000); write_prg(bank); } else { set_address(0xB000); write_prg((bank<<1)+0); set_address(0xD000); write_prg((bank<<1)+1); } break; case 18: if(mem == PRG) { set_address(0x8000); write_prg(bank<<1); set_address(0x8001); write_prg((bank<<1)>>4); set_address(0x8002); write_prg((bank<<1)+1); set_address(0x8003); write_prg(((bank<<1)+1)>>4); } else { set_address(0xA000); write_prg((bank<<3)+0); set_address(0xA001); write_prg(((bank<<3)+0)>>4); set_address(0xA002); write_prg((bank<<3)+1); set_address(0xA003); write_prg(((bank<<3)+1)>>4); set_address(0xB000); write_prg((bank<<3)+2); set_address(0xB001); write_prg(((bank<<3)+2)>>4); set_address(0xB002); write_prg((bank<<3)+3); set_address(0xB003); write_prg(((bank<<3)+3)>>4); set_address(0xC000); write_prg((bank<<3)+4); set_address(0xC001); write_prg(((bank<<3)+4)>>4); set_address(0xC002); write_prg((bank<<3)+5); set_address(0xC003); write_prg(((bank<<3)+5)>>4); set_address(0xD000); write_prg((bank<<3)+6); set_address(0xD001); write_prg(((bank<<3)+6)>>4); set_address(0xD002); write_prg((bank<<3)+7); set_address(0xD003); write_prg(((bank<<3)+7)>>4); } break; case 26: if(mem == PRG) { set_address(0x8000); write_prg(bank); } else { set_address(0xD000); write_prg((bank<<3)+0); set_address(0xD002); write_prg((bank<<3)+1); set_address(0xD001); write_prg((bank<<3)+2); set_address(0xD003); write_prg((bank<<3)+3); set_address(0xE000); write_prg((bank<<3)+5); set_address(0xE002); write_prg((bank<<3)+7); set_address(0xE001); write_prg((bank<<3)+8); set_address(0xE003); write_prg((bank<<3)+9); } break; case 65: if(mem == PRG) { set_address(0x9000);write_prg(0x00); set_address(0x8000); write_prg((bank<<1)+0); set_address(0xa000); write_prg((bank<<1)+1); set_address(0xc000); write_prg((bank<<1)+2); } else { set_address(0xB000); write_prg((bank<<3)+0); set_address(0xB001); write_prg((bank<<3)+1); set_address(0xB002); write_prg((bank<<3)+2); set_address(0xB003); write_prg((bank<<3)+3); set_address(0xB004); write_prg((bank<<3)+4); set_address(0xB005); write_prg((bank<<3)+5); set_address(0xB006); write_prg((bank<<3)+6); set_address(0xB007); write_prg((bank<<3)+7); } break; case 70: if(mem == PRG) { set_address(0x8000); write_prg(bank<<4); } else { set_address(0x8000); write_prg(bank); } break; case 75: if(mem == PRG) { set_address(0x8000); write_prg((bank<<1)+0); set_address(0xA000); set_address(0xA000); write_prg((bank<<1)+1); }else { set_address(0x9000); write_prg((bank>>3) * 0x06); set_address(0xE000); write_prg((((bank<<1)+0)&0xf)); set_address(0xF000); write_prg((((bank<<1)+1)&0xf)); } break; case 77: cc=2; if(mem == PRG) { set_address(0x8000);write_prg(bank); }else{ set_address(0x8000);write_prg(bank<<4); } break; case 87: if(mem == CHR) { set_address(0x6000);write_prg(bank<<1); } break; case 184: if(mem ==CHR){ set_address(0x6000);write_prg(bank*2+(bank*2+1)*16); } break; case 1016: if(bank < 16) { set_address(0x8000); write_prg(0x00); set_address(0x8001); write_prg(0x00); set_address(0x8002); write_prg(0x00); set_address(0x8003); write_prg(0x00); set_address(0x8004); write_prg(0x00); set_address(0x8005); write_prg(0x00); set_address(0x8006); write_prg(0x00); set_address(0x8007); write_prg(0x00); set_address(0x8008); write_prg(bank); } else { set_address(0x8000); write_prg(0x01); set_address(0x8001); write_prg(0x01); set_address(0x8002); write_prg(0x01); set_address(0x8003); write_prg(0x01); set_address(0x8004); write_prg(0x01); set_address(0x8005); write_prg(0x01); set_address(0x8006); write_prg(0x01); set_address(0x8007); write_prg(0x01); set_address(0x8008); write_prg((bank)-16); } break; } } unsigned int checksum(int start, int end, int mem) { unsigned int chk = 0; int bank, byte, val,val2; set_address(start * 0x100); for(bank = start; bank <= end; bank++) { for(byte = 0; byte <= 0xFF; byte++) { val = read_register(); if(verify) { DELAY; val2 = read_register(); if(val != val2) fprintf(stderr, "\nError reading addr 0x%02X%02X:\t%02X %02X\n", bank, byte,val,val2); } chk += val; INC_ADR; } } return chk; } int count_banks(int mem) { int bank; unsigned int first, last, chk; if(mem == CHR && test_vram() != 0) return 0; if(type == 0) { if(mem == PRG) { READ_PRG; if(checksum(0x00, 0x40-1, PRG) != checksum(0x40, 0x80-1, PRG)) return 2; } return 1; } printf("Trying to count %s banks...\n ", (mem == PRG ? "PRG (16k)" : "CHR (8k)")); if(mem == PRG) { READ_PRG; } else { READ_CHR; } for(bank = 0; bank < MAXBANKS; bank++) { switch_bank(bank, mem); if(mem == PRG) { READ_PRG; } else { READ_CHR; } if(mem == CHR) chk = checksum(0x00, 0x20-1, mem); else chk = checksum(0x00, 0x40-1, mem); printf("(%d:%X) ", bank, chk); if(bank == 0) first = last = chk; else { /* if(chk == first || chk == last) { */ if(chk == first ) { printf("Found %d %s banks\n", bank, (mem == PRG ? "PRG" : "CHR")); return bank; } last = chk; } } printf("No %s banks (?)\n", (mem == PRG ? "PRG" : "CHR")); return 0; } int test_vram(void) { unsigned char i,tmp; printf("Looking for VRAM... "); /* switch_bank(0, CHR); */ set_address(0x0000); READ_CHR; i = read_register(); write_chr(0x00); OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA5);\ if((tmp = read_register()) != 0x00){ printf("00:%02X test1 ng\n",tmp); return 0; }else{ } write_chr(0xFF); OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA5);\ if((tmp = read_register()) != 0xFF){ printf("00:%02X test2 ng\n",tmp); return 0; }else{ } write_chr(i); write_register(i); printf("VRAM present\n"); return 1; } void read_prg(FILE *fp, int nbbank, int banksize) { int bank, page, byte; unsigned int chk; unsigned char val,val2; banksize <<= 2; READ_PRG; for(bank = 0; bank < nbbank; bank++) { printf("Reading bank %d\n", bank); READ_PRG; switch_bank(bank, PRG); READ_PRG; chk = 0; set_address(0x0000); for(page = 0x00; page < banksize; page++) { printf("."); for(byte = 0; byte <= 0xFF; byte++) { /* OUTPORT(A_8255_PA, byte); i = INPORT(A_8255_PC); */ val = read_register(); if(verify) { DELAY; val2 = read_register(); if(val != val2) fprintf(stderr, "\nError reading addr 0x%02X:%02X%02X:\t%02X %02X\n",bank,page, byte,val,val2); } chk += val; fputc(val, fp); INC_ADR; } } printf(" (%X)\n", chk); } } void read_chr(FILE *fp, int nbbank, int banksize) { int bank, page, byte; unsigned int chk; unsigned char val,val2; banksize <<= 2; READ_CHR; for(bank = 0; bank < nbbank; bank++) { printf("Reading bank %d\n", bank); READ_CHR; switch_bank(bank, CHR); READ_CHR; chk = 0; set_address(0x0000); for(page = 0x00; page < banksize; page++) { printf("."); for(byte = 0; byte <= 0xFF; byte++) { /* OUTPORT(A_8255_PA, byte); i = INPORT(B_8255_PB); */ val= read_register(); if(verify) { DELAY; val2 = read_register(); if(val != val2) fprintf(stderr, "\nError reading addr 0x%02X:%02X%02X:\t%02X %02X\n",page, bank, byte,val,val2); } chk += val; fputc(val, fp); INC_ADR; } } printf(" (%X)\n", chk); } } void init_romreader(void) { OUTPORT(PRT_CONTROL,INPORT(PRT_CONTROL)|STROBE); /*shift register clear H*/ OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA0); /*counter clock H*/ OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA1); /*register clock L*/ OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA2); /*serial input reset*/ OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA5|DATA6|DATA7); RESET_ADR; } void set_address(unsigned int address) { unsigned int i; if(address >0x7fff) address = address - 0x8000; RESET_ADR; for(i =0;i>7)<>1; } } void write_prg(unsigned char val){ OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA4|DATA5|DATA6|DATA7); OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA4); write_register(val); OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA7); OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA4); OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA6); OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA4); OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA6); OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA7); OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA4); /* #define write_prg(val, addr) \ CLK L set address set val /WE L CLK? H PRG /OE L CLK? L PRG /OE H PROG /WE H CLK H */ } void write_chr(unsigned char val){ OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA4|DATA5|DATA6|DATA7);\ write_register(val);\ OUTPORT(PRT_DATA,INPORT(PRT_DATA)&~DATA4);\ OUTPORT(PRT_DATA,INPORT(PRT_DATA)|DATA4); } void main(int argc, char **argv) { int arg; FILE *fp; int nb_rom = -1; int nb_vrom = -1; if(argc < 2) { usage(argv[0]); exit(1); } type = 0; verify = 0; wait_delay = 10; xx=16; init_romreader(); for(arg = 1; arg < argc; arg++) { if(argv[arg][0] != '-') { usage(argv[0]); exit(1); } switch(argv[arg][1]) { case 'v': case 'V': verify++; break; case 'b': case 'B': type = atoi(argv[++arg]); printf("type :%d\n",type); break; case 'p': case 'P': /* card_address = (atoi(argv[++arg]) == 1 ? 0x310 : 0x300); */ break; case 'w': case 'W': wait_delay = atoi(argv[++arg]); break; case 'a': case 'A': switch_init(); printf("PRG :%d banks\n",count_banks(PRG)); printf("CHR :%d banks\n",count_banks(CHR)); break; case 'X': case 'x': xx=32; printf("1bank:PRG(32k)ごとに設定\n"); break; case 'n': case 'N': switch(argv[arg][2]) { case 'c': case 'C': nb_vrom = atoi(argv[++arg]); break; case 'p': case 'P': nb_rom = atoi(argv[++arg]); break; default: usage(argv[0]); exit(1); } break; case 's': case 'S': if((fp = fopen(argv[arg+1], "wb")) == NULL) { printf("Error opening file %s\n", argv[arg+1]); exit(1); } switch(argv[arg][2]) { case 'c': case 'C': { switch_init(); if(nb_vrom < 0) nb_vrom = count_banks(CHR); /* One bank is 8kB */ read_chr(fp, nb_vrom, cc); } break; case 'p': case 'P': { switch_init(); if(nb_rom < 0) nb_rom = count_banks(PRG); /* One bank is 16k bytes */ if(type == 0 && nb_rom == 2) read_prg(fp, 1, 32); else read_prg(fp, nb_rom, xx); } break; case 'n': case 'N': default: { unsigned char hdr[16]; switch_init(); if(nb_rom < 0) nb_rom = count_banks(PRG); if(nb_vrom < 0) nb_vrom = count_banks(CHR); memset(hdr, 0, 16); strcpy(hdr, "NES\032"); hdr[4] = (unsigned char)nb_rom; hdr[5] = (unsigned char)nb_vrom; hdr[6] = (unsigned char)(type<<4); fwrite(hdr, 16, 1, fp); /* One bank is 16kB */ /* if((type == 0 && nb_rom == 2)||(type == 3 &&nb_rom ==2)) */ if(nb_rom == 2) read_prg(fp, 1, 32); else read_prg(fp, nb_rom, xx); /* One bank is 8kB */ read_chr(fp, nb_vrom, cc); } break; } fclose(fp); arg++; break; case 't': case 'T': test_vram(); break; default: usage(argv[0]); exit(1); } } exit(0); }