| #include "rockmacros.h" |
| #include "defs.h" |
| #include "regs.h" |
| #include "mem.h" |
| #include "hw.h" |
| #include "lcd-gb.h" |
| #include "rtc-gb.h" |
| #include "save.h" |
| #include "sound.h" |
| |
| /* From http://www.semis.demon.co.uk/Gameboy/Gbspec.txt (4/17/2007) |
| * Cartridge type: |
| * 0 - ROM ONLY 12 - ROM+MBC3+RAM |
| * 1 - ROM+MBC1 13 - ROM+MBC3+RAM+BATT |
| * 2 - ROM+MBC1+RAM 19 - ROM+MBC5 |
| * 3 - ROM+MBC1+RAM+BATT 1A - ROM+MBC5+RAM |
| * 5 - ROM+MBC2 1B - ROM+MBC5+RAM+BATT |
| * 6 - ROM+MBC2+BATTERY 1C - ROM+MBC5+RUMBLE |
| * 8 - ROM+RAM 1D - ROM+MBC5+RUMBLE+SRAM |
| * 9 - ROM+RAM+BATTERY 1E - ROM+MBC5+RUMBLE+SRAM+BATT |
| * B - ROM+MMM01 1F - Pocket Camera |
| * C - ROM+MMM01+SRAM FD - Bandai TAMA5 |
| * D - ROM+MMM01+SRAM+BATT FE - Hudson HuC-3 |
| * F - ROM+MBC3+TIMER+BATT FF - Hudson HuC-1 |
| * 10 - ROM+MBC3+TIMER+RAM+BATT |
| * 11 - ROM+MBC3 |
| */ |
| |
| static int mbc_table[256] = |
| { |
| MBC_NONE, |
| MBC_MBC1, |
| MBC_MBC1, |
| MBC_MBC1 | MBC_BAT, |
| 0, |
| MBC_MBC2, |
| MBC_MBC2 | MBC_BAT, |
| 0, |
| 0, |
| MBC_BAT, |
| 0, |
| 0, |
| 0, |
| MBC_BAT, |
| 0, |
| MBC_MBC3 | MBC_BAT | MBC_RTC, |
| MBC_MBC3 | MBC_BAT | MBC_RTC, |
| MBC_MBC3, |
| MBC_MBC3, |
| MBC_MBC3 | MBC_BAT, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| MBC_MBC5, |
| MBC_MBC5, |
| MBC_MBC5 | MBC_BAT, |
| MBC_RUMBLE, |
| MBC_RUMBLE, |
| MBC_RUMBLE | MBC_BAT, |
| 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| MBC_HUC3, |
| MBC_HUC1 |
| }; |
| |
| static unsigned short romsize_table[56] = |
| { |
| 2, 4, 8, 16, 32, 64, 128, 256, |
| 512, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 128, 128, 128, 0 |
| /* 0, 0, 0, 0, 72, 80, 96 -- actual values but bad to use these! */ |
| }; |
| |
| /* Ram size should be no larger then 16 banks 1Mbit */ |
| static unsigned char ramsize_table[5] = |
| { |
| 0, 1, 1, 4, 16 |
| }; |
| |
| static char *romfile; |
| static char sramfile[500]; |
| static char rtcfile[500]; |
| static char saveprefix[500]; |
| |
| static int saveslot; |
| |
| static int forcebatt, nobatt; |
| static int forcedmg; |
| |
| static int memfill = -1, memrand = -1; |
| |
| static void initmem(void *mem, int size) |
| { |
| char *p = mem; |
| if (memrand >= 0) |
| { |
| srand(memrand ? memrand : -6 ); /* time(0)); */ |
| while(size--) *(p++) = rand(); |
| } |
| else if (memfill >= 0) |
| memset(p, memfill, size); |
| } |
| |
| static byte *loadfile(int fd, int *len) |
| { |
| int c; |
| byte *d; |
| |
| *len=lseek(fd,0,SEEK_END); |
| d=malloc((*len)*sizeof(char)+64); |
| if(d==0) |
| { |
| die("Not enough memory"); |
| return 0; |
| } |
| lseek(fd,0,SEEK_SET); |
| |
| c = read(fd, d, *len); |
| |
| return d; |
| } |
| |
| int rom_load(void) |
| { |
| int fd; |
| byte c, *data, *header; |
| int len = 0, rlen; |
| |
| fd = open(romfile, O_RDONLY); |
| |
| if (fd<0) |
| { |
| die("cannot open rom file %s", romfile); |
| return 1; |
| } |
| |
| data = loadfile(fd, &len); |
| close(fd); |
| if(data==0) |
| { |
| die("Not Enough Memory"); |
| return 1; |
| } |
| header = data; /* no zip. = decompress(data, &len); */ |
| |
| memcpy(rom.name, header+0x0134, 16); |
| if (rom.name[14] & 0x80) rom.name[14] = 0; |
| if (rom.name[15] & 0x80) rom.name[15] = 0; |
| rom.name[16] = 0; |
| |
| c = header[0x0147]; |
| mbc.type = mbc_table[c]&(MBC_MBC1|MBC_MBC2|MBC_MBC3|MBC_MBC5|MBC_RUMBLE|MBC_HUC1|MBC_HUC3); |
| mbc.batt = ((mbc_table[c]&MBC_BAT) && !nobatt) || forcebatt; |
| rtc.batt = mbc_table[c]&MBC_RTC; |
| |
| if(header[0x0148]<10 || (header[0x0148]>51 && header[0x0148]<55)) |
| mbc.romsize = romsize_table[header[0x0148]]; |
| else |
| { |
| die("unknown ROM size %02X\n", header[0x0148]); |
| return 1; |
| } |
| |
| if(header[0x0149]<5) |
| mbc.ramsize = ramsize_table[header[0x0149]]; |
| else |
| { |
| die("unknown SRAM size %02X\n", header[0x0149]); |
| return 1; |
| } |
| |
| rlen = 16384 * mbc.romsize; |
| rom.bank = (void *) data; /* realloc(data, rlen); */ |
| if (rlen > len) memset(rom.bank[0]+len, 0xff, rlen - len); |
| |
| /* This is the size of the ram on the cartridge |
| * See http://www.semis.demon.co.uk/Gameboy/Gbspec.txt |
| * for a full description. (8192*number of banks) |
| */ |
| ram.sbank = malloc(8192 * mbc.ramsize); |
| if(ram.sbank==0 && mbc.ramsize!=0) |
| { |
| die("Not enough Memory"); |
| return 1; |
| } |
| |
| /* ram.ibank = malloc(4096*8); */ |
| |
| initmem(ram.sbank, 8192 * mbc.ramsize); |
| initmem(ram.ibank, 4096 * 8); |
| |
| mbc.rombank = 1; |
| mbc.rambank = 0; |
| |
| c = header[0x0143]; |
| hw.cgb = ((c == 0x80) || (c == 0xc0)) && !forcedmg; |
| |
| return 0; |
| } |
| |
| int sram_load(void) |
| { |
| int fd; |
| char meow[500]; |
| |
| if (!mbc.batt || !*sramfile) return -1; |
| |
| /* Consider sram loaded at this point, even if file doesn't exist */ |
| ram.loaded = 1; |
| |
| fd = open(sramfile, O_RDONLY); |
| snprintf(meow,499,"Opening %s %d",sramfile,fd); |
| rb->splash(HZ*2, meow); |
| if (fd<0) return -1; |
| snprintf(meow,499,"Loading savedata from %s",sramfile); |
| rb->splash(HZ*2, meow); |
| read(fd,ram.sbank, 8192*mbc.ramsize); |
| close(fd); |
| |
| return 0; |
| } |
| |
| |
| int sram_save(void) |
| { |
| int fd; |
| char meow[500]; |
| |
| /* If we crash before we ever loaded sram, DO NOT SAVE! */ |
| if (!mbc.batt || !ram.loaded || !mbc.ramsize) |
| return -1; |
| fd = open(sramfile, O_WRONLY|O_CREAT|O_TRUNC); |
| if (fd<0) return -1; |
| snprintf(meow,499,"Saving savedata to %s",sramfile); |
| rb->splash(HZ*2, meow); |
| write(fd,ram.sbank, 8192*mbc.ramsize); |
| close(fd); |
| |
| return 0; |
| } |
| |
| |
| void state_save(int n) |
| { |
| int fd; |
| char name[500]; |
| |
| if (n < 0) n = saveslot; |
| if (n < 0) n = 0; |
| snprintf(name, 499,"%s.%03d", saveprefix, n); |
| |
| if ((fd = open(name, O_WRONLY|O_CREAT|O_TRUNC)>=0)) |
| { |
| savestate(fd); |
| close(fd); |
| } |
| } |
| |
| |
| void state_load(int n) |
| { |
| int fd; |
| char name[500]; |
| |
| if (n < 0) n = saveslot; |
| if (n < 0) n = 0; |
| snprintf(name, 499, "%s.%03d", saveprefix, n); |
| |
| if ((fd = open(name, O_RDONLY)>=0)) |
| { |
| loadstate(fd); |
| close(fd); |
| vram_dirty(); |
| pal_dirty(); |
| sound_dirty(); |
| mem_updatemap(); |
| } |
| } |
| |
| void rtc_save(void) |
| { |
| int fd; |
| if (!rtc.batt) return; |
| if ((fd = open(rtcfile, O_WRONLY|O_CREAT|O_TRUNC))<0) return; |
| rtc_save_internal(fd); |
| close(fd); |
| } |
| |
| void rtc_load(void) |
| { |
| int fd; |
| if (!rtc.batt) return; |
| if ((fd = open(rtcfile, O_RDONLY))<0) return; |
| rtc_load_internal(fd); |
| close(fd); |
| } |
| |
| |
| void loader_unload(void) |
| { |
| sram_save(); |
| /* if (romfile) free(romfile); |
| if (sramfile) free(sramfile); |
| if (saveprefix) free(saveprefix); |
| if (rom.bank) free(rom.bank); |
| if (ram.sbank) free(ram.sbank); */ |
| romfile = 0; |
| rom.bank = 0; |
| ram.sbank = 0; |
| mbc.type = mbc.romsize = mbc.ramsize = mbc.batt = 0; |
| } |
| |
| void cleanup(void) |
| { |
| sram_save(); |
| rtc_save(); |
| /* IDEA - if error, write emergency savestate..? */ |
| } |
| |
| void loader_init(char *s) |
| { |
| romfile = s; |
| if(rom_load()) |
| return; |
| rb->splash(HZ/2, rom.name); |
| |
| snprintf(saveprefix, 499, "%s/%s", savedir, rom.name); |
| |
| strcpy(sramfile, saveprefix); |
| strcat(sramfile, ".sav"); |
| |
| strcpy(rtcfile, saveprefix); |
| strcat(rtcfile, ".rtc"); |
| |
| sram_load(); |
| rtc_load(); |
| } |