diff --git a/license.txt b/license.txt index 0f3bc4f5..ade2c9a5 100644 --- a/license.txt +++ b/license.txt @@ -66,7 +66,8 @@ such software. libco, author: byuu libui, author: byuu OBC-1 emu, author: byuu -S-DD1 emu, author: Andreas Naive +S-DD1 decompressor, author: Andreas Naive +SPC7110 decompressor, author: neviksti S-RTC emu, author: byuu Any software listed above as exemptions may be relicensed individually from diff --git a/readme.txt b/readme.txt index 2243f5d4..a4f9716a 100644 --- a/readme.txt +++ b/readme.txt @@ -1,10 +1,10 @@ bsnes -Version: 0.032a +Version: 0.033 Author: byuu --------- +======== General: --------- +======== bsnes is a Super Nintendo / Super Famicom emulator that began on October 14th, 2004. @@ -13,9 +13,9 @@ http://byuu.org/ Please see license.txt for important licensing information. --------------- +============== Configuration: --------------- +============== bsnes has two configuration files: bsnes.cfg, for program settings; and locale.cfg, for localization. @@ -36,9 +36,9 @@ If you wish to have multiple configuration profiles for the same user, you will need to make copies of the bsnes executable, and use each one in single-user mode. ------------------- +================== Known Limitations: ------------------- +================== S-CPU - Multiply / divide register delays not implemented @@ -56,9 +56,9 @@ Hardware Bugs - S-CPU.r1 HDMA crashing bug not emulated - S-CPU<>S-SMP communication bus conflicts not emulated ---------------------- +===================== Unsupported Hardware: ---------------------- +===================== SA-1 Coprocessor used in many popular games, including: - Dragon Ball Z Hyper Dimension @@ -75,13 +75,6 @@ Coprocessor used in many popular games, including: - Star Fox 2 (unreleased beta) - Super Mario World 2: Yoshi's Island -SPC7110 -Coprocessor used only by the following games: -- Far East of Eden Zero -- Far East of Eden Zero: Shounen Jump no Shou -- Momotarou Densetsu Happy -- Super Power League 4 - ST-011 SETA DSP used by Quick-move Shogi Match with Nidan Rank-holder Morita @@ -91,9 +84,9 @@ SETA RISC CPU used by Quick-move Shogi Match with Nidan Rank-holder Morita 2 Super Gameboy Cartridge passthrough used for playing Gameboy games ------------------------- +======================== Unsupported Controllers: ------------------------- +======================== Mouse Super Scope Justifier diff --git a/src/Makefile b/src/Makefile index a33359dd..42400545 100644 --- a/src/Makefile +++ b/src/Makefile @@ -79,7 +79,7 @@ link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`) objects = main libco hiro ruby libfilter string reader cart cheat \ memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \ - bsx srtc sdd1 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 + bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 ifeq ($(enable_gzip),true) objects += adler32 compress crc32 deflate gzio inffast inflate inftrees ioapi trees unzip zip zutil @@ -196,16 +196,17 @@ obj/snes.$(obj): snes/snes.cpp snes/* snes/scheduler/* snes/video/* snes/audio/* ### special chips ### ##################### -obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/* -obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/* -obj/sdd1.$(obj) : chip/sdd1/sdd1.cpp chip/sdd1/* -obj/cx4.$(obj) : chip/cx4/cx4.cpp chip/cx4/* -obj/dsp1.$(obj) : chip/dsp1/dsp1.cpp chip/dsp1/* -obj/dsp2.$(obj) : chip/dsp2/dsp2.cpp chip/dsp2/* -obj/dsp3.$(obj) : chip/dsp3/dsp3.cpp chip/dsp3/* -obj/dsp4.$(obj) : chip/dsp4/dsp4.cpp chip/dsp4/* -obj/obc1.$(obj) : chip/obc1/obc1.cpp chip/obc1/* -obj/st010.$(obj): chip/st010/st010.cpp chip/st010/* +obj/bsx.$(obj) : chip/bsx/bsx.cpp chip/bsx/* +obj/srtc.$(obj) : chip/srtc/srtc.cpp chip/srtc/* +obj/sdd1.$(obj) : chip/sdd1/sdd1.cpp chip/sdd1/* +obj/spc7110.$(obj): chip/spc7110/spc7110.cpp chip/spc7110/* +obj/cx4.$(obj) : chip/cx4/cx4.cpp chip/cx4/* +obj/dsp1.$(obj) : chip/dsp1/dsp1.cpp chip/dsp1/* +obj/dsp2.$(obj) : chip/dsp2/dsp2.cpp chip/dsp2/* +obj/dsp3.$(obj) : chip/dsp3/dsp3.cpp chip/dsp3/* +obj/dsp4.$(obj) : chip/dsp4/dsp4.cpp chip/dsp4/* +obj/obc1.$(obj) : chip/obc1/obc1.cpp chip/obc1/* +obj/st010.$(obj) : chip/st010/st010.cpp chip/st010/* ############ ### zlib ### diff --git a/src/base.h b/src/base.h index 8dfc5801..05b843bb 100644 --- a/src/base.h +++ b/src/base.h @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.032a" +#define BSNES_VERSION "0.033" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define BUSCORE sBus diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp index 1406e442..02998530 100644 --- a/src/cart/cart.cpp +++ b/src/cart/cart.cpp @@ -13,7 +13,7 @@ #include "cart_header.cpp" namespace memory { - MappedRAM cartrom, cartram; + MappedRAM cartrom, cartram, cartrtc; MappedRAM bscram; MappedRAM stArom, stAram; MappedRAM stBrom, stBram; @@ -27,12 +27,12 @@ Cartridge::Region Cartridge::region() { return info.region; } bool Cartridge::loaded() { return cart.loaded; } void Cartridge::load_begin(CartridgeType cart_type) { - cart.rom = cart.ram = 0; + cart.rom = cart.ram = cart.rtc = 0; bs.ram = 0; stA.rom = stA.ram = 0; stB.rom = stB.ram = 0; - cart.rom_size = cart.ram_size = 0; + cart.rom_size = cart.ram_size = cart.rtc_size = 0; bs.ram_size = 0; stA.rom_size = stA.ram_size = 0; stB.rom_size = stB.ram_size = 0; @@ -44,20 +44,21 @@ void Cartridge::load_begin(CartridgeType cart_type) { info.bsxflash = false; info.st = false; - info.superfx = false; - info.sa1 = false; - info.spc7110 = false; - info.srtc = false; - info.sdd1 = false; - info.cx4 = false; - info.dsp1 = false; - info.dsp2 = false; - info.dsp3 = false; - info.dsp4 = false; - info.obc1 = false; - info.st010 = false; - info.st011 = false; - info.st018 = false; + info.superfx = false; + info.sa1 = false; + info.srtc = false; + info.sdd1 = false; + info.spc7110 = false; + info.spc7110rtc = false; + info.cx4 = false; + info.dsp1 = false; + info.dsp2 = false; + info.dsp3 = false; + info.dsp4 = false; + info.obc1 = false; + info.st010 = false; + info.st011 = false; + info.st018 = false; info.dsp1_mapper = DSP1Unmapped; @@ -73,6 +74,7 @@ void Cartridge::load_begin(CartridgeType cart_type) { void Cartridge::load_end() { memory::cartrom.map(cart.rom, cart.rom_size); memory::cartram.map(cart.ram, cart.ram_size); + memory::cartrtc.map(cart.rtc, cart.rtc_size); memory::bscram.map(bs.ram, bs.ram_size); memory::stArom.map(stA.rom, stA.rom_size); memory::stAram.map(stA.ram, stA.ram_size); @@ -110,6 +112,7 @@ bool Cartridge::unload() { if(cart.rom) { delete[] cart.rom; cart.rom = 0; } if(cart.ram) { delete[] cart.ram; cart.ram = 0; } + if(cart.rtc) { delete[] cart.rtc; cart.rtc = 0; } if(bs.ram) { delete[] bs.ram; bs.ram = 0; } if(stA.rom) { delete[] stA.rom; stA.rom = 0; } if(stA.ram) { delete[] stA.ram; stA.ram = 0; } diff --git a/src/cart/cart.h b/src/cart/cart.h index 2e42e87a..433dc352 100644 --- a/src/cart/cart.h +++ b/src/cart/cart.h @@ -32,6 +32,7 @@ public: HiROM, ExLoROM, ExHiROM, + SPC7110ROM, BSXROM, BSCLoROM, BSCHiROM, @@ -48,8 +49,8 @@ public: struct { bool loaded; char fn[PATH_MAX]; - uint8 *rom, *ram; - uint rom_size, ram_size; + uint8 *rom, *ram, *rtc; + uint rom_size, ram_size, rtc_size; } cart; struct { @@ -85,6 +86,7 @@ public: bool srtc; bool sdd1; bool spc7110; + bool spc7110rtc; bool cx4; bool dsp1; bool dsp2; @@ -142,12 +144,13 @@ public: private: char patchfn[PATH_MAX]; - char savefn[PATH_MAX]; + char savefn[PATH_MAX]; + char rtcfn[PATH_MAX]; char cheatfn[PATH_MAX]; }; namespace memory { - extern MappedRAM cartrom, cartram; + extern MappedRAM cartrom, cartram, cartrtc; extern MappedRAM bscram; extern MappedRAM stArom, stAram; extern MappedRAM stBrom, stBram; diff --git a/src/cart/cart_header.cpp b/src/cart/cart_header.cpp index 3461bbf1..dadf0b04 100644 --- a/src/cart/cart_header.cpp +++ b/src/cart/cart_header.cpp @@ -52,8 +52,9 @@ void Cartridge::read_header() { } if(mapper == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { - //rom_type: 0xf5 = no S-RTC, 0xf9 = S-RTC info.spc7110 = true; + info.spc7110rtc = (rom_type == 0xf9); + info.mapper = SPC7110ROM; } if(mapper == 0x20 && rom_type == 0xf3) { @@ -179,7 +180,7 @@ void Cartridge::find_header() { score_ex = 0; } else { if(rom[0x7fc0 + MAPPER] == 0x32) score_lo++; - else score_ex += 16; + else score_ex += 12; } if(score_lo >= score_hi && score_lo >= score_ex) { diff --git a/src/cart/cart_normal.cpp b/src/cart/cart_normal.cpp index 3460cc0f..945b1586 100644 --- a/src/cart/cart_normal.cpp +++ b/src/cart/cart_normal.cpp @@ -40,6 +40,14 @@ void Cartridge::load_cart_normal(const char *filename) { } } + if(info.srtc || info.spc7110rtc) { + cart.rtc = new(zeromemory) uint8_t[cart.rtc_size = 20]; + if(load_file(get_save_filename(cart.fn, "rtc"), data, size, CompressionNone) == true) { + memcpy(cart.rtc, data, min(size, cart.rtc_size)); + delete[] data; + } + } + load_end(); //set base filename @@ -49,6 +57,7 @@ void Cartridge::load_cart_normal(const char *filename) { void Cartridge::unload_cart_normal() { if(cart.ram) save_file(get_save_filename(cart.fn, "srm"), cart.ram, cart.ram_size); + if(cart.rtc) save_file(get_save_filename(cart.fn, "rtc"), cart.rtc, cart.rtc_size); } #endif //ifdef CART_CPP diff --git a/src/chip/chip.h b/src/chip/chip.h index f28ce348..f3393af6 100644 --- a/src/chip/chip.h +++ b/src/chip/chip.h @@ -1,6 +1,7 @@ #include "bsx/bsx.h" #include "srtc/srtc.h" #include "sdd1/sdd1.h" +#include "spc7110/spc7110.h" #include "cx4/cx4.h" #include "dsp1/dsp1.h" #include "dsp2/dsp2.h" diff --git a/src/chip/spc7110/codec.cpp b/src/chip/spc7110/codec.cpp new file mode 100644 index 00000000..e24047ca --- /dev/null +++ b/src/chip/spc7110/codec.cpp @@ -0,0 +1,697 @@ +const uint8_t SPC7110Codec::EvolutionTable[53][4] = { + //prob, nextlps, nextmps, toggle invert + {0x5a, 1, 1,1}, //0 l,m + {0x25, 6, 2,0}, //1 l,m + {0x11, 8, 3,0}, //2 l,m + {0x08, 10, 4,0}, //3 ,m + {0x03, 12, 5,0}, //4 ,m + {0x01, 15, 5,0}, //5 ,m + + {0x5a, 7, 7,1}, //6 l, + {0x3f, 19, 8,0}, //7 l,m + {0x2c, 21, 9,0}, //8 l,m + {0x20, 22, 10,0}, //9 ,m + {0x17, 23, 11,0}, //10 ,m + {0x11, 25, 12,0}, //11 ,m + {0x0c, 26, 13,0}, //12 ,m + {0x09, 28, 14,0}, //13 ,m + {0x07, 29, 15,0}, //14 ,m + {0x05, 31, 16,0}, //15 ,m + {0x04, 32, 17,0}, //16 ,m + {0x03, 34, 18,0}, //17 ,m + {0x02, 35, 5,0}, //18 ,m + + {0x5a, 20, 20,1}, //19 l,m + {0x48, 39, 21,0}, //20 l,m + {0x3a, 40, 22,0}, //21 l,m + {0x2e, 42, 23,0}, //22 l,m + {0x26, 44, 24,0}, //23 l,m + {0x1f, 45, 25,0}, //24 l,m + {0x19, 46, 26,0}, //25 l,m + {0x15, 25, 27,0}, //26 l,m + {0x11, 26, 28,0}, //27 l,m + {0x0e, 26, 29,0}, //28 l,m + {0x0b, 27, 30,0}, //29 ,m + {0x09, 28, 31,0}, //30 ,m + {0x08, 29, 32,0}, //31 l,m + {0x07, 30, 33,0}, //32 l,m + {0x05, 31, 34,0}, //33 l,m <--- changed lps + {0x04, 33, 35,0}, //34 ,m ... this is NOT skipped + {0x04, 33, 36,0}, //35 ,m + {0x03, 34, 37,0}, //36 ,m + {0x02, 35, 38,0}, //37 ,m ... this is NOT skipped + {0x02, 36, 5,0}, //38 ,m + + {0x58, 39, 40,1}, //39 l,m + {0x4d, 47, 41,0}, //40 l,m + {0x43, 48, 42,0}, //41 ,m + {0x3b, 49, 43,0}, //42 ,m + {0x34, 50, 44,0}, //43 l,m + {0x2e, 51, 45,0}, //44 l,m + {0x29, 44, 46,0}, //45 l,m + {0x25, 45, 24,0}, //46 ,m + + {0x56, 47, 48,1}, //47 l,m + {0x4f, 47, 49,0}, //48 l,m + {0x47, 48, 50,0}, //49 l,m + {0x41, 49, 51,0}, //50 l,m + {0x3c, 50, 52,0}, //51 l,m + {0x37, 51, 43,0} //52 ,m +}; + +const uint8_t SPC7110Codec::Mode2ContextTable[32][4] = { + // "bit" = (lps^invert) + //next_0, use ref pixel, next_1, use ref pixel + // if use ref pixel, then add on the 0-4 bell number grouping + {1, 0, 2, 0}, //0 + + {3, 1, 8, 1}, //1 prev bit 0 + {13,0, 14,0}, //2 prev bit 1 + + {15,0, 16,0}, //3 prev bit 00 + {17,0, 18,0}, //4 + {19,0, 20,0}, //5 + {21,0, 22,0}, //6 + {23,0, 24,0}, //7 + {25,0, 26,0}, //8 prev bit 01 + {25,0, 26,0}, //9 + {25,0, 26,0}, //10 + {25,0, 26,0}, //11 + {25,0, 26,0}, //12 + {27,0, 28,0}, //13 prev bit 10 + {29,0, 30,0}, //14 prev bit 11 + + {31,0, 31,0}, //15 000 ref group 0 + {31,0, 31,0}, //16 001 ref group 0 + {31,0, 31,0}, //17 000 ref group 1 + {31,0, 31,0}, //18 001 ref group 1 + {31,0, 31,0}, //19 000 ref group 2 + {31,0, 31,0}, //20 001 ref group 2 + {31,0, 31,0}, //21 000 ref group 3 + {31,0, 31,0}, //22 001 ref group 3 + {31,0, 31,0}, //23 000 ref group 4 + {31,0, 31,0}, //24 001 ref group 4 + {31,0, 31,0}, //25 010 + {31,0, 31,0}, //26 011 + {31,0, 31,0}, //27 100 + {31,0, 31,0}, //28 101 + {31,0, 31,0}, //29 110 + {31,0, 31,0}, //30 111 + + {31,0, 31,0} //31 -- used as a trap for testing purposes -- +}; + +#define PROB(x) EvolutionTable[Contexts[x].index][0] +#define NEXT_LPS(x) EvolutionTable[Contexts[x].index][1] +#define NEXT_MPS(x) EvolutionTable[Contexts[x].index][2] +#define TOGGLE_INVERT(x) EvolutionTable[Contexts[x].index][3] +#define BIT(x,y) ((x>>y)&1) + +void SPC7110Codec::decomp_mode0(int len) { + uint8_t *datain = buffer; + uint8_t *dataout = output; + static const unsigned NUM_CONTEXTS = 30; + + uint8 top,val; + uint8 con,mps,prob; + uint8 flag_lps,shift,mask; + + int out=0; + int inverts=0; + int lps=0; + + unsigned char in; + int in_count; + + int i,bit; + + //setup + top=0xFF; + + val=*datain; + datain++; + + in=*datain; + datain++; + in_count=8; + + //reset context states + for(i=0;i3) + con+=15; + + //get PROB and MPS + prob = PROB(con); + mps = (BIT(out,15) ^ Contexts[con].invert); + + if(i>=15 && i<=18 && 0) + printf("byte %d bit %d: val=%.2X top=%.2X prob=%.2X mps=%d con=%d state=%d\n", + i,bit,val,top,prob,mps,con,Contexts[con].index); + + //get bit + if (val <= top-prob) + { + //mps + top = top - prob; + out = (out << 1) + mps; + + flag_lps=0; + } + else + { + //lps + val = val - (top - (prob - 1)); + top = prob - 1; + out = (out << 1) + 1-mps; + + flag_lps=1; + } + + // renormalize + shift=0; + while(top<0x7F) // NOTE: not 0x80, it's a strange border case + { + shift++; + + top = (top<<1)+1; + val = (val<<1)+(in>>7); + + in = (in<<1); + if(--in_count==0) + { + in=*datain; + datain++; + in_count=8; + } + } + + //update processing info + lps = (lps<<1) + flag_lps; + inverts = (inverts<<1) + Contexts[con].invert; + + //update context state + if(flag_lps & TOGGLE_INVERT(con)) + Contexts[con].invert ^= 1; + + if(flag_lps) + { + //SeenEvolution[Contexts[con].index][0]=1; + Contexts[con].index = NEXT_LPS(con); + } + else if(shift) + { + //SeenEvolution[Contexts[con].index][1]=1; + Contexts[con].index = NEXT_MPS(con); + } + } + + //save byte + *dataout = (out & 0xFF); + dataout++; + } +} + +void SPC7110Codec::decomp_mode1(int len) { + uint8_t *datain = buffer; + uint8_t *dataout = output; + static const unsigned NUM_CONTEXTS = 15; + + int pixelorder[4]={0,1,2,3}; + int realorder[4]; + int a,b,c; + int m,n; + + uint8 top,val; + uint8 con,prob; + uint8 flag_lps,shift; + + int out=0; + int inverts=0; + int lps=0; + + unsigned char in; + int in_count; + int in_len=0; + + int i,j,pixel; + + //setup + top=0xFF; + + val=datain[in_len++]; + + in=datain[in_len++]; + in_count=8; + + //reset context states + for(i=0;i> (1*2)) & 0x3); + b = ((out >> (7*2)) & 0x3); + c = ((out >> (8*2)) & 0x3); + if(a==b && b==c) + con=0; + else if (a==b && b!=c) + con=1; + else if (a!=b && b==c) + con=2; + else if (a==c && b!=c) + con=3; + else + con=4; + + //update pixel order + for(m=0;m<4;m++) + if(pixelorder[m]==a) + break; + for(n=m;n>0;n--) + { + j=pixelorder[n-1]; + pixelorder[n-1]=pixelorder[n]; + pixelorder[n]=j; + } + + + //get PROB + prob = PROB(con); + + //get symbol + if (val <= top-prob) + { + //mps + top = top - prob; + flag_lps=0; + } + else + { + //lps + val = val - (top - (prob - 1)); + top = prob - 1; + flag_lps=1; + } + + // renormalize + shift=0; + while(top<0x7F) + { + shift++; + + top = (top<<1)+1; + val = (val<<1)+(in>>7); + + in = (in<<1); + if(--in_count==0) + { + in=datain[in_len++]; + in_count=8; + } + } + + //update processing info + lps = (lps<<1) + flag_lps; + inverts = (inverts<<1) + Contexts[con].invert; + + //update context state + if(flag_lps & TOGGLE_INVERT(con)) + Contexts[con].invert ^= 1; + if(flag_lps) + Contexts[con].index = NEXT_LPS(con); + else if(shift) + Contexts[con].index = NEXT_MPS(con); + + //get context of second symbol + con = 5 + con*2 + ((lps^inverts)&1); + + //get PROB + prob = PROB(con); + + //get symbol + if (val <= top-prob) + { + //mps + top = top - prob; + flag_lps=0; + } + else + { + //lps + val = val - (top - (prob - 1)); + top = prob - 1; + flag_lps=1; + } + + // renormalize + shift=0; + while(top<0x7F) + { + shift++; + + top = (top<<1)+1; + val = (val<<1)+(in>>7); + + in = (in<<1); + if(--in_count==0) + { + in=datain[in_len++]; + in_count=8; + } + } + + //calculate the real pixel order + for(m=0;m<4;m++) + realorder[m]=pixelorder[m]; + //shift refence pixel c value to top + for(m=0;m<4;m++) + if(realorder[m]==c) + break; + for(n=m;n>0;n--) + { + j=realorder[n-1]; + realorder[n-1]=realorder[n]; + realorder[n]=j; + } + //shift refence pixel b value to top + for(m=0;m<4;m++) + if(realorder[m]==b) + break; + for(n=m;n>0;n--) + { + j=realorder[n-1]; + realorder[n-1]=realorder[n]; + realorder[n]=j; + } + //shift refence pixel a value to top + for(m=0;m<4;m++) + if(realorder[m]==a) + break; + for(n=m;n>0;n--) + { + j=realorder[n-1]; + realorder[n-1]=realorder[n]; + realorder[n]=j; + } + + + //update processing info + lps = (lps<<1) + flag_lps; + inverts = (inverts<<1) + Contexts[con].invert; + + //update context state + if(flag_lps & TOGGLE_INVERT(con)) + Contexts[con].invert ^= 1; + if(flag_lps) + Contexts[con].index = NEXT_LPS(con); + else if(shift) + Contexts[con].index = NEXT_MPS(con); + + //get pixel + b=realorder[(lps^inverts)&3]; + out = (out<<2) + b; + } + } + + //turn pixel data into bitplanes + //and save as output.. BUT don't save second byte unless asked to + *dataout = (BIT(out,15)<<7) + (BIT(out,13)<<6) + (BIT(out,11)<<5) + (BIT(out,9)<<4) + + (BIT(out,7)<<3) + (BIT(out,5)<<2) + (BIT(out,3)<<1) + BIT(out,1); + dataout++; + if((len&1)==0) + { + *dataout = (BIT(out,14)<<7) + (BIT(out,12)<<6) + (BIT(out,10)<<5) + (BIT(out,8)<<4) + + (BIT(out,6)<<3) + (BIT(out,4)<<2) + (BIT(out,2)<<1) + BIT(out,0); + dataout++; + } + + if(in_count==8) + in_len--; + //printf("Used %d bytes of input.\n",in_len); + //return in_len; +} + +void SPC7110Codec::decomp_mode2(int len) { + uint8_t *datain = buffer; + uint8_t *dataout = output; + static const unsigned NUM_CONTEXTS = 32; + + int pixelorder[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + int realorder[16]; + int a,b,c; + int m,n; + + uint8 bitplanebuffer[16]; + uint8 buf_idx=0; + + uint8 top,val,prob; + uint8 con,refcon; + uint8 flag_lps,invertbit,shift; + + int out=0; + int out2=0; + int inverts=0; + int lps=0; + + unsigned char in; + int in_count; + int in_len=0; + + int i,j,pixel,bit; + + //setup + top=0xFF; + + val=datain[in_len++]; + + in=datain[in_len++]; + in_count=8; + + //reset context states + for(i=0;i> (0*4)) & 0x0F); + b = ((out >> (7*4)) & 0x0F); + c = ((out2>> (0*4)) & 0x0F); + if(a==b && b==c) + refcon=0; + else if (a==b && b!=c) + refcon=1; + else if (a!=b && b==c) + refcon=2; + else if (a==c && b!=c) + refcon=3; + else + refcon=4; + + con=0; + + //update pixel order + for(m=0;m<16;m++) + if(pixelorder[m]==a) + break; + for(n=m;n>0;n--) + { + j=pixelorder[n-1]; + pixelorder[n-1]=pixelorder[n]; + pixelorder[n]=j; + } + + //calculate the real pixel order + for(m=0;m<16;m++) + realorder[m]=pixelorder[m]; + //shift refence pixel c value to top + for(m=0;m<16;m++) + if(realorder[m]==c) + break; + for(n=m;n>0;n--) + { + j=realorder[n-1]; + realorder[n-1]=realorder[n]; + realorder[n]=j; + } + //shift refence pixel b value to top + for(m=0;m<16;m++) + if(realorder[m]==b) + break; + for(n=m;n>0;n--) + { + j=realorder[n-1]; + realorder[n-1]=realorder[n]; + realorder[n]=j; + } + //shift refence pixel a value to top + for(m=0;m<16;m++) + if(realorder[m]==a) + break; + for(n=m;n>0;n--) + { + j=realorder[n-1]; + realorder[n-1]=realorder[n]; + realorder[n]=j; + } + + //get 4 symbols + for(bit=0;bit<4;bit++) + { + //get PROB + prob = PROB(con); + + //get symbol + if (val <= top-prob) + { + //mps + top = top - prob; + flag_lps=0; + } + else + { + //lps + val = val - (top - (prob - 1)); + top = prob - 1; + flag_lps=1; + } + + // renormalize + shift=0; + while(top<0x7F) + { + shift++; + + top = (top<<1)+1; + val = (val<<1)+(in>>7); + + in = (in<<1); + if(--in_count==0) + { + in=datain[in_len++]; + in_count=8; + } + } + + //update processing info + lps = (lps<<1) + flag_lps; + invertbit = Contexts[con].invert; + inverts = (inverts<<1) + Contexts[con].invert; + + //update context state + if(flag_lps & TOGGLE_INVERT(con)) + Contexts[con].invert ^= 1; + if(flag_lps) + Contexts[con].index = NEXT_LPS(con); + else if(shift) + Contexts[con].index = NEXT_MPS(con); + + //get next context + if(Mode2ContextTable[con][2*(flag_lps^invertbit)+1]) + con=Mode2ContextTable[con][2*(flag_lps^invertbit)]+refcon; + else + con=Mode2ContextTable[con][2*(flag_lps^invertbit)]; + } + + //get pixel + b=realorder[(lps^inverts)&0x0F]; + out2 = (out2<<4) + ((out>>28)&0x0F); + out = (out<<4) + b; + } + + //cludge to convert pixel data into bitplanes and respect len parameter for output buf + *dataout = (BIT(out,31)<<7) + (BIT(out,27)<<6) + (BIT(out,23)<<5) + (BIT(out,19)<<4) + + (BIT(out,15)<<3) + (BIT(out,11)<<2) + (BIT(out,7)<<1) + BIT(out,3); + dataout++; + + if((i+1)= size) addr -= size; + return addr + 0x100000; +} + +unsigned SPC7110::data_pointer() { return r4811 + (r4812 << 8) + (r4813 << 16); } +unsigned SPC7110::data_adjust() { return r4814 + (r4815 << 8); } +unsigned SPC7110::data_increment() { return r4816 + (r4817 << 8); } +void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; } +void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; } + +void SPC7110::update_time(int offset) { + time_t rtc_time; + rtc_time = memory::cartrtc.read(16); + rtc_time |= memory::cartrtc.read(17) << 8; + rtc_time |= memory::cartrtc.read(18) << 16; + rtc_time |= memory::cartrtc.read(19) << 24; + + bool update = true; + if(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set + if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set + + time_t current_time = time(0) - offset; + if(update && current_time > rtc_time) { + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; + unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8) + memory::cartrtc.read( 9) * 10; + unsigned year = memory::cartrtc.read(10) + memory::cartrtc.read(11) * 10; + unsigned weekday = memory::cartrtc.read(12); + + day--; + month--; + year += (year >= 90) ? 1900 : 2000; //range = 1990-2089 + + second += (unsigned)(current_time - rtc_time); + while(second >= 60) { + second -= 60; + + minute++; + if(minute < 60) continue; + minute = 0; + + hour++; + if(hour < 24) continue; + hour = 0; + + day++; + weekday = (weekday + 1) % 7; + unsigned days = months[month % 12]; + if(days == 28) { + bool leapyear = false; + if((year % 4) == 0) { + leapyear = true; + if((year % 100) == 0 && (year % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + if(day < days) continue; + day = 0; + + month++; + if(month < 12) continue; + month = 0; + + year++; + } + + day++; + month++; + year %= 100; + + memory::cartrtc.write( 0, second % 10); + memory::cartrtc.write( 1, second / 10); + memory::cartrtc.write( 2, minute % 10); + memory::cartrtc.write( 3, minute / 10); + memory::cartrtc.write( 4, hour % 10); + memory::cartrtc.write( 5, hour / 10); + memory::cartrtc.write( 6, day % 10); + memory::cartrtc.write( 7, day / 10); + memory::cartrtc.write( 8, month % 10); + memory::cartrtc.write( 9, month / 10); + memory::cartrtc.write(10, year % 10); + memory::cartrtc.write(11, (year / 10) % 10); + memory::cartrtc.write(12, weekday % 7); + } + + memory::cartrtc.write(16, current_time); + memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(18, current_time >> 16); + memory::cartrtc.write(19, current_time >> 24); +} + +uint8 SPC7110::mmio_read(uint addr) { + addr &= 0xffff; + + switch(addr) { + //================== + //decompression unit + //================== + + case 0x4800: { + uint16 counter = (r4809 + (r480a << 8)); + counter--; + r4809 = counter; + r480a = counter >> 8; + return codec.output[(decomp_offset++) & 0xffff]; + } + case 0x4801: return r4801; + case 0x4802: return r4802; + case 0x4803: return r4803; + case 0x4804: return r4804; + case 0x4805: return r4805; + case 0x4806: return r4806; + case 0x4807: return r4807; + case 0x4808: return r4808; + case 0x4809: return r4809; + case 0x480a: return r480a; + case 0x480b: return r480b; + case 0x480c: { + uint8 status = r480c; + r480c &= 0x7f; + return status; + } + + //============== + //data port unit + //============== + + case 0x4810: { + if(r481x != 0x1f) return 0x00; + + unsigned addr = data_pointer(); + unsigned adjust = data_adjust(); + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + + unsigned adjustaddr = addr; + if(r4818 & 2) { + adjustaddr += adjust; + set_data_adjust(adjust + 1); + } + + uint8 data = memory::cartrom.read(datarom_addr(adjustaddr)); + if(!(r4818 & 2)) { + unsigned increment = (r4818 & 1) ? data_increment() : 1; + if(r4818 & 4) increment = (int16)increment; //16-bit sign extend + + if((r4818 & 16) == 0) { + set_data_pointer(addr + increment); + } else { + set_data_adjust(adjust + increment); + } + } + + return data; + } + case 0x4811: return r4811; + case 0x4812: return r4812; + case 0x4813: return r4813; + case 0x4814: return r4814; + case 0x4815: return r4815; + case 0x4816: return r4816; + case 0x4817: return r4817; + case 0x4818: return r4818; + case 0x481a: { + if(r481x != 0x1f) return 0x00; + + unsigned addr = data_pointer(); + unsigned adjust = data_adjust(); + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + + uint8 data = memory::cartrom.read(datarom_addr(addr + adjust)); + if((r4818 & 0x60) == 0x60) { + if((r4818 & 16) == 0) { + set_data_pointer(addr + adjust); + } else { + set_data_adjust(adjust + adjust); + } + } + + return data; + } + + //========= + //math unit + //========= + + case 0x4820: return r4820; + case 0x4821: return r4821; + case 0x4822: return r4822; + case 0x4823: return r4823; + case 0x4824: return r4824; + case 0x4825: return r4825; + case 0x4826: return r4826; + case 0x4827: return r4827; + case 0x4828: return r4828; + case 0x4829: return r4829; + case 0x482a: return r482a; + case 0x482b: return r482b; + case 0x482c: return r482c; + case 0x482d: return r482d; + case 0x482e: return r482e; + case 0x482f: { + uint8 status = r482f; + r482f &= 0x7f; + return status; + } + + //=================== + //memory mapping unit + //=================== + + case 0x4830: return r4830; + case 0x4831: return r4831; + case 0x4832: return r4832; + case 0x4833: return r4833; + case 0x4834: return r4834; + + //==================== + //real-time clock unit + //==================== + + case 0x4840: return r4840; + case 0x4841: { + if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00; + + r4842 = 0x80; + uint8 data = memory::cartrtc.read(rtc_index); + rtc_index = (rtc_index + 1) & 15; + return data; + } + case 0x4842: { + uint8 status = r4842; + r4842 &= 0x7f; + return status; + } + } + + return cpu.regs.mdr; +} + +void SPC7110::mmio_write(uint addr, uint8 data) { + addr &= 0xffff; + + switch(addr) { + //================== + //decompression unit + //================== + + case 0x4801: r4801 = data; break; + case 0x4802: r4802 = data; break; + case 0x4803: r4803 = data; break; + case 0x4804: r4804 = data; break; + case 0x4805: r4805 = data; break; + case 0x4806: { + r4806 = data; + + unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16)); + unsigned index = (r4804 << 2); + unsigned length = (r4809 + (r480a << 8)); + unsigned addr = datarom_addr(table + index); + unsigned mode = (memory::cartrom.read(addr + 0)); + unsigned offset = (memory::cartrom.read(addr + 1) << 16) + + (memory::cartrom.read(addr + 2) << 8) + + (memory::cartrom.read(addr + 3) << 0); + + //this can technically be 65536, but it has never been observed higher than 32768 ... + //really need a way to determine both compressed and decompressed lengths, though. + static const unsigned max_length = 32768; + + offset = datarom_addr(offset); + for(unsigned i = 0; i < max_length; i++) codec.buffer[i] = memory::cartrom.read(offset + i); + + #if 0 + printf("decompression: 4805=$%0.2x,4806=$%0.2x,4807=$%0.2x,4808=$%0.2x,480b=$%0.2x\n", + r4805, r4806, r4807, r4808, r480b); + printf("table=$%0.6x,index=%3d,length=%5d,mode=%d,offset=$%0.6x\n", + table, r4804, length, mode, offset); + #endif + + switch(mode) { + case 0: codec.decomp_mode0(max_length); break; + case 1: codec.decomp_mode1(max_length); break; + case 2: codec.decomp_mode2(max_length); break; + } + + decomp_offset = (r4805 + (r4806 << 8)) << mode; + r480c = 0x80; + } break; + + case 0x4807: r4807 = data; break; + case 0x4808: r4808 = data; break; + case 0x4809: r4809 = data; break; + case 0x480a: r480a = data; break; + case 0x480b: r480b = data; break; + + //============== + //data port unit + //============== + + case 0x4811: r4811 = data; r481x |= 0x01; break; + case 0x4812: r4812 = data; r481x |= 0x02; break; + case 0x4813: r4813 = data; r481x |= 0x04; break; + case 0x4814: { + r4814 = data; + r481x |= 0x08; + r4814_latch = true; + if(!r4815_latch) break; + if(!(r4818 & 2)) break; + if(r4818 & 0x10) break; + + if((r4818 & 0x60) == 0x20) { + unsigned increment = data_adjust() & 0xff; + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + set_data_pointer(data_pointer() + increment); + } else if((r4818 & 0x60) == 0x40) { + unsigned increment = data_adjust(); + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + set_data_pointer(data_pointer() + increment); + } + } break; + case 0x4815: { + r4815 = data; + r481x |= 0x10; + r4815_latch = true; + if(!r4814_latch) break; + if(!(r4818 & 2)) break; + if(r4818 & 0x10) break; + + if((r4818 & 0x60) == 0x20) { + unsigned increment = data_adjust() & 0xff; + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + set_data_pointer(data_pointer() + increment); + } else if((r4818 & 0x60) == 0x40) { + unsigned increment = data_adjust(); + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + set_data_pointer(data_pointer() + increment); + } + } break; + case 0x4816: r4816 = data; break; + case 0x4817: r4817 = data; break; + case 0x4818: { + if(r481x != 0x1f) break; + + r4818 = data; + r4814_latch = r4815_latch = false; + } break; + + //========= + //math unit + //========= + + case 0x4820: r4820 = data; break; + case 0x4821: r4821 = data; break; + case 0x4822: r4822 = data; break; + case 0x4823: r4823 = data; break; + case 0x4824: r4824 = data; break; + case 0x4825: { + r4825 = data; + + if(r482e & 1) { + //signed 16-bit x 16-bit multiplication + int16 r0 = (int16)(r4824 + (r4825 << 8)); + int16 r1 = (int16)(r4820 + (r4821 << 8)); + + signed result = r0 * r1; + r4828 = result; + r4829 = result >> 8; + r482a = result >> 16; + r482b = result >> 24; + } else { + //unsigned 16-bit x 16-bit multiplication + uint16 r0 = (uint16)(r4824 + (r4825 << 8)); + uint16 r1 = (uint16)(r4820 + (r4821 << 8)); + + unsigned result = r0 * r1; + r4828 = result; + r4829 = result >> 8; + r482a = result >> 16; + r482b = result >> 24; + } + + r482f = 0x80; + } break; + case 0x4826: r4826 = data; break; + case 0x4827: { + r4827 = data; + + if(r482e & 1) { + //signed 32-bit x 16-bit division + int32 dividend = (int32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24)); + int16 divisor = (int16)(r4826 + (r4827 << 8)); + + int32 quotient; + int16 remainder; + + if(divisor) { + quotient = (int32)(dividend / divisor); + remainder = (int32)(dividend % divisor); + } else { + //illegal division by zero + quotient = 0; + remainder = dividend & 0xffff; + } + + r4828 = quotient; + r4829 = quotient >> 8; + r482a = quotient >> 16; + r482b = quotient >> 24; + + r482c = remainder; + r482d = remainder >> 8; + } else { + //unsigned 32-bit x 16-bit division + uint32 dividend = (uint32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24)); + uint16 divisor = (uint16)(r4826 + (r4827 << 8)); + + uint32 quotient; + uint16 remainder; + + if(divisor) { + quotient = (uint32)(dividend / divisor); + remainder = (uint16)(dividend % divisor); + } else { + //illegal division by zero + quotient = 0; + remainder = dividend & 0xffff; + } + + r4828 = quotient; + r4829 = quotient >> 8; + r482a = quotient >> 16; + r482b = quotient >> 24; + + r482c = remainder; + r482d = remainder >> 8; + } + + r482f = 0x80; + } break; + + case 0x482e: { + //reset math unit + r4820 = r4821 = r4822 = r4823 = 0; + r4824 = r4825 = r4826 = r4827 = 0; + r4828 = r4829 = r482a = r482b = 0; + r482c = r482d = 0; + + r482e = data; + } break; + + //=================== + //memory mapping unit + //=================== + + case 0x4830: r4830 = data; break; + + case 0x4831: { + r4831 = data; + dx_offset = datarom_addr((data & 7) * 0x100000); + } break; + + case 0x4832: { + r4832 = data; + ex_offset = datarom_addr((data & 7) * 0x100000); + } break; + + case 0x4833: { + r4833 = data; + fx_offset = datarom_addr((data & 7) * 0x100000); + } break; + + case 0x4834: r4834 = data; break; + + //==================== + //real-time clock unit + //==================== + + case 0x4840: { + r4840 = data; + if(!(r4840 & 1)) { + //disable RTC + rtc_state = RTCS_Inactive; + update_time(); + } else { + //enable RTC + r4842 = 0x80; + rtc_state = RTCS_ModeSelect; + } + } break; + + case 0x4841: { + r4841 = data; + + switch(rtc_state) { + case RTCS_ModeSelect: { + if(data == RTCM_Linear || data == RTCM_Indexed) { + r4842 = 0x80; + rtc_state = RTCS_IndexSelect; + rtc_mode = (RTC_Mode)data; + rtc_index = 0; + } + } break; + + case RTCS_IndexSelect: { + r4842 = 0x80; + rtc_index = data & 15; + if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write; + } break; + + case RTCS_Write: { + r4842 = 0x80; + + //control register 0 + if(rtc_index == 13) { + //increment second counter + if(data & 2) update_time(+1); + + //round minute counter + if(data & 8) { + update_time(); + + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + //clear seconds + memory::cartrtc.write(0, 0); + memory::cartrtc.write(1, 0); + + if(second >= 30) update_time(+60); + } + } + + //control register 2 + if(rtc_index == 15) { + //disable timer and clear second counter + if((data & 1) && !(memory::cartrtc.read(15) & 1)) { + update_time(); + + //clear seconds + memory::cartrtc.write(0, 0); + memory::cartrtc.write(1, 0); + } + + //disable timer + if((data & 2) && !(memory::cartrtc.read(15) & 2)) { + update_time(); + } + } + + memory::cartrtc.write(rtc_index, data & 15); + rtc_index = (rtc_index + 1) & 15; + } break; + } //switch(rtc_state) + } break; + } +} + +uint8 SPC7110::read(uint addr) { + if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { + //$[00|30]:[6000-7fff] + return memory::cartram.read(addr & 0x1fff); + } + + if((addr & 0x708000) == 0x008000) { + //$[00-0f|80-8f]:[8000-ffff] + return memory::cartrom.read(addr & 0x0fffff); + } + + if((addr & 0xff0000) == 0x500000) { + //$[50]:[0000-ffff] + return mmio_read(0x4800); + } + + if((addr & 0xf00000) == 0xc00000) { + //$[c0-cf]:[0000-ffff] + return memory::cartrom.read(addr & 0x0fffff); + } + + if((addr & 0xf00000) == 0xd00000) { + //$[d0-df]:[0000-ffff] + return memory::cartrom.read(dx_offset + (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xe00000) { + //$[e0-ef]:[0000-ffff] + return memory::cartrom.read(ex_offset + (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xf00000) { + //$[f0-ff]:[0000-ffff] + return memory::cartrom.read(fx_offset + (addr & 0x0fffff)); + } + + return cpu.regs.mdr; +} + +void SPC7110::write(uint addr, uint8 data) { + if((addr & 0xffe000) == 0x006000 || (addr & 0xffe000) == 0x306000) { + //$[00|30]:[6000-7fff] + if(r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data); + return; + } +} + +SPC7110::SPC7110() { +} diff --git a/src/chip/spc7110/spc7110.h b/src/chip/spc7110/spc7110.h new file mode 100644 index 00000000..6694e186 --- /dev/null +++ b/src/chip/spc7110/spc7110.h @@ -0,0 +1,130 @@ +/***** + * SPC7110 emulator - version 0.1 (2008-07-19) + * Copyright (c) 2008, byuu and neviksti + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * The software is provided "as is" and the author disclaims all warranties + * with regard to this software including all implied warranties of + * merchantibility and fitness, in no event shall the author be liable for + * any special, direct, indirect, or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether in an + * action of contract, negligence or other tortious action, arising out of + * or in connection with the use or performance of this software. + *****/ + +#include "codec.h" + +class SPC7110 : public MMIO, public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + unsigned datarom_addr(unsigned addr); + + unsigned data_pointer(); + unsigned data_adjust(); + unsigned data_increment(); + void set_data_pointer(unsigned addr); + void set_data_adjust(unsigned addr); + + void update_time(int offset = 0); + time_t create_time(); + + uint8 mmio_read (uint addr); + void mmio_write(uint addr, uint8 data); + + uint8 read (uint addr); + void write(uint addr, uint8 data); + + SPC7110(); + +private: + //================== + //decompression unit + //================== + uint8 r4801; //compression table low + uint8 r4802; //compression table high + uint8 r4803; //compression table bank + uint8 r4804; //compression table index + uint8 r4805; //decompression buffer index low + uint8 r4806; //decompression buffer index high + uint8 r4807; //??? + uint8 r4808; //??? + uint8 r4809; //compression length low + uint8 r480a; //compression length high + uint8 r480b; //decompression control register + uint8 r480c; //decompression status + + SPC7110Codec codec; + uint16 decomp_offset; + + //============== + //data port unit + //============== + uint8 r4811; //data pointer low + uint8 r4812; //data pointer high + uint8 r4813; //data pointer bank + uint8 r4814; //data adjust low + uint8 r4815; //data adjust high + uint8 r4816; //data increment low + uint8 r4817; //data increment high + uint8 r4818; //data port control register + + uint8 r481x; + + bool r4814_latch; + bool r4815_latch; + + //========= + //math unit + //========= + uint8 r4820; //16-bit multiplicand B0, 32-bit dividend B0 + uint8 r4821; //16-bit multiplicand B1, 32-bit dividend B1 + uint8 r4822; //32-bit dividend B2 + uint8 r4823; //32-bit dividend B3 + uint8 r4824; //16-bit multiplier B0 + uint8 r4825; //16-bit multiplier B1 + uint8 r4826; //16-bit divisor B0 + uint8 r4827; //16-bit divisor B1 + uint8 r4828; //32-bit product B0, 32-bit quotient B0 + uint8 r4829; //32-bit product B1, 32-bit quotient B1 + uint8 r482a; //32-bit product B2, 32-bit quotient B2 + uint8 r482b; //32-bit product B3, 32-bit quotient B3 + uint8 r482c; //16-bit remainder B0 + uint8 r482d; //16-bit remainder B1 + uint8 r482e; //math control register + uint8 r482f; //math status + + //=================== + //memory mapping unit + //=================== + uint8 r4830; //SRAM write enable + uint8 r4831; //$[d0-df]:[0000-ffff] mapping + uint8 r4832; //$[e0-ef]:[0000-ffff] mapping + uint8 r4833; //$[f0-ff]:[0000-ffff] mapping + uint8 r4834; //??? + + unsigned dx_offset; + unsigned ex_offset; + unsigned fx_offset; + + //==================== + //real-time clock unit + //==================== + uint8 r4840; //RTC latch + uint8 r4841; //RTC index/data port + uint8 r4842; //RTC status + + enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write } rtc_state; + enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c } rtc_mode; + unsigned rtc_index; + + static const unsigned months[12]; +}; + +extern SPC7110 spc7110; diff --git a/src/chip/srtc/srtc.cpp b/src/chip/srtc/srtc.cpp index 32e81741..e688da23 100644 --- a/src/chip/srtc/srtc.cpp +++ b/src/chip/srtc/srtc.cpp @@ -1,189 +1,213 @@ -/* - S-RTC chip emulation - Used by Hudson Soft in Dai Kaijuu Monogatari II and Far East of Eden Zero. - Currently, only the former is supported by bsnes. - - Original S-RTC emulation code via John Weidman/SNES9x - Rewritten for compatibility with bsnes via byuu - - The S-RTC is a real-time clock chip that was added to the above two carts - to allow the games to maintain the current time, even when the game was not - powered on. Thus allowing special events at certain times, and on certain - dates. Hudson Soft called this the PLG (Player's Life Gameplay System). - - This chip is a special case to the term 'emulation' itself. - There are a few different ways to go about emulating this chip, and each - result in a different style of emulation. - - The first is to simply return the current PC system time when the S-RTC is - read from. This emulates the original S-RTC in the sense that it always - returns the true current time, ignoring the speed that the SNES itself is - running at. The downside to this method is that you lose the ability to set - the time to whatever you choose inside the game itself. It will always return - the true time, regardless. This can be overcome by changing the PC system time, - which actually adds a greater degree of control over event timing, very useful - for emulation. It also has a timeshifting flaw discussed below. - - The second is to run the S-RTC relative to the SNES speed. This means that - if the emulator is sped up (via fast forward key, frameskipping, etc), or - slowed down (via slowdown key, system bottlenecking, etc); the time increments - slower, thus ~60 frames on the SNES equal one second. Without this, timeshifting - will occur between the S-RTC and the real SNES. - - The third and final method is to save a copy of the local system time when the - S-RTC is initially set, and compare the current system time against this value - when setting the S-RTC time. This overcomes the first methods' shortcoming of - not allowing the player to set the time in-game, however a new problem arises. - You now have to save the time when the RTC was initially set to both savestates - and to save-game data. This would require an extra file, or the breaking of - perhaps the only standard format (.srm savegame backups) in the entire SNES - emulation scene. You also give up the control of being able to override the - RTC clock at will via the PC system time outside of emulation. - The first method has another advantage over the third: Dai Kaijuu Monogatari II - only allows dates in the range of the years 1996-2199. The first method gets - around this limitation. But who knows, maybe it will break something in the - game if the date exceeds 2199... I guess we'll worry about that in two hundred - years from now. - - For my implementation, I chose to go with the first method. Both for simplicity - and because I did not wish to create a new method for saving the system time - whenever the RTC is set. -*/ - -#include "../../base.h" - -void SRTC::set_time() { -time_t rawtime; -tm *t; - ::time(&rawtime); - t = localtime(&rawtime); - -//see srtc.h for format of srtc.data[] - srtc.data[0] = t->tm_sec % 10; - srtc.data[1] = t->tm_sec / 10; - srtc.data[2] = t->tm_min % 10; - srtc.data[3] = t->tm_min / 10; - srtc.data[4] = t->tm_hour % 10; - srtc.data[5] = t->tm_hour / 10; - srtc.data[6] = t->tm_mday % 10; - srtc.data[7] = t->tm_mday / 10; - srtc.data[8] = t->tm_mon + 1; - srtc.data[9] = t->tm_year % 10; - srtc.data[10] = (t->tm_year / 10) % 10; - srtc.data[11] = 9 + (t->tm_year / 100); - srtc.data[12] = t->tm_wday; -} - -void SRTC::init() {} - -void SRTC::enable() { - memory::mmio.map(0x2800, *this); - memory::mmio.map(0x2801, *this); -} - -void SRTC::power() { - memset(&srtc, 0, sizeof(srtc)); - reset(); -} - -void SRTC::reset() { - srtc.index = -1; - srtc.mode = SRTC_READ; -} - -uint8 SRTC::mmio_read(uint addr) { - switch(addr & 0xffff) { - - case 0x2800: { - if(srtc.mode == SRTC_READ) { - if(srtc.index < 0) { - set_time(); - srtc.index++; - return 0x0f; //send start message - } else if(srtc.index > MAX_SRTC_INDEX) { - srtc.index = -1; - return 0x0f; //send finished message - } else { - return srtc.data[srtc.index++]; - } - } else { - return 0x00; - } - } break; - - case 0x2801: { - } break; - - } - - return cpu.regs.mdr; -} - -//Please see notes above about the implementation of the S-RTC -//Writes are stored the srtc.data[] array, but they are ignored -//as reads will refresh the data array with the current system -//time. The write method is only here for the sake of faux -//emulation of the real hardware. -void SRTC::mmio_write(uint addr, uint8 data) { - switch(addr & 0xffff) { - - case 0x2800: { - } break; - - case 0x2801: { - data &= 0x0f; //only the low four bits are used - - if(data >= 0x0d) { - switch(data) { - case 0x0d: - srtc.mode = SRTC_READ; - srtc.index = -1; - break; - case 0x0e: - srtc.mode = SRTC_COMMAND; - break; - case 0x0f: - //unknown behaviour - break; - } - return; - } - - if(srtc.mode == SRTC_WRITE) { - if(srtc.index >= 0 && srtc.index < MAX_SRTC_INDEX) { - srtc.data[srtc.index++] = data; - - if(srtc.index == MAX_SRTC_INDEX) { - //all S-RTC data has been loaded by program - srtc.data[srtc.index++] = 0x00; //day_of_week - } - } - } else if(srtc.mode == SRTC_COMMAND) { - switch(data) { - case SRTC_COMMAND_CLEAR: - memset(srtc.data, 0, MAX_SRTC_INDEX + 1); - srtc.index = -1; - srtc.mode = SRTC_READY; - break; - case SRTC_COMMAND_WRITE: - srtc.index = 0; - srtc.mode = SRTC_WRITE; - break; - default: - //unknown behaviour - srtc.mode = SRTC_READY; - break; - } - } else { - if(srtc.mode == SRTC_READ) { - //ignore writes while in read mode - } else if(srtc.mode == SRTC_READY) { - //unknown behaviour - } - } - } break; - - } -} - -SRTC::SRTC() {} +#include "../../base.h" + +const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +void SRTC::init() { +} + +void SRTC::enable() { + memory::mmio.map(0x2800, *this); + memory::mmio.map(0x2801, *this); +} + +void SRTC::power() { + reset(); +} + +void SRTC::reset() { + rtc_mode = RTCM_Read; + rtc_index = -1; + update_time(); +} + +void SRTC::update_time() { + time_t rtc_time; + rtc_time = memory::cartrtc.read(16); + rtc_time |= memory::cartrtc.read(17) << 8; + rtc_time |= memory::cartrtc.read(18) << 16; + rtc_time |= memory::cartrtc.read(19) << 24; + + time_t current_time = time(0); + if(current_time > rtc_time) { + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; + unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8); + unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100; + unsigned weekday = memory::cartrtc.read(12); + + day--; + month--; + year += 1000; + + second += (unsigned)(current_time - rtc_time); + + while(second >= 60) { + second -= 60; + + minute++; + if(minute < 60) continue; + minute = 0; + + hour++; + if(hour < 24) continue; + hour = 0; + + day++; + weekday = (weekday + 1) % 7; + unsigned days = months[month % 12]; + if(days == 28) { + bool leapyear = false; + if((year % 4) == 0) { + leapyear = true; + if((year % 100) == 0 && (year % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + if(day < days) continue; + day = 0; + + month++; + if(month < 12) continue; + month = 0; + + year++; + } + + day++; + month++; + year -= 1000; + + memory::cartrtc.write( 0, second % 10); + memory::cartrtc.write( 1, second / 10); + memory::cartrtc.write( 2, minute % 10); + memory::cartrtc.write( 3, minute / 10); + memory::cartrtc.write( 4, hour % 10); + memory::cartrtc.write( 5, hour / 10); + memory::cartrtc.write( 6, day % 10); + memory::cartrtc.write( 7, day / 10); + memory::cartrtc.write( 8, month); + memory::cartrtc.write( 9, year % 10); + memory::cartrtc.write(10, (year / 10) % 10); + memory::cartrtc.write(11, year / 100); + memory::cartrtc.write(12, weekday % 7); + } + + memory::cartrtc.write(16, current_time); + memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(18, current_time >> 16); + memory::cartrtc.write(19, current_time >> 24); +} + +//returns day of week for specified date +//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday +//usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008 +unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) { + unsigned y = 1900, m = 1; //epoch is 1900-01-01 + unsigned sum = 0; //number of days passed since epoch + + year = max(1900, year); + month = max(1, min(12, month)); + day = max(1, min(31, day)); + + while(y < year) { + bool leapyear = false; + if((y % 4) == 0) { + leapyear = true; + if((y % 100) == 0 && (y % 400) != 0) leapyear = false; + } + sum += leapyear ? 366 : 365; + y++; + } + + while(m < month) { + unsigned days = months[m - 1]; + if(days == 28) { + bool leapyear = false; + if((y % 4) == 0) { + leapyear = true; + if((y % 100) == 0 && (y % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + sum += days; + m++; + } + + sum += day - 1; + return (sum + 1) % 7; //1900-01-01 was a Monday +} + +uint8 SRTC::mmio_read(uint addr) { + addr &= 0xffff; + + if(addr == 0x2800) { + if(rtc_mode != RTCM_Read) return 0x00; + + if(rtc_index < 0) { + update_time(); + rtc_index++; + return 0x0f; + } else if(rtc_index > 12) { + rtc_index = -1; + return 0x0f; + } else { + return memory::cartrtc.read(rtc_index++); + } + } + + return cpu.regs.mdr; +} + +void SRTC::mmio_write(uint addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x2801) { + data &= 0x0f; //only the low four bits are used + + if(data == 0x0d) { + rtc_mode = RTCM_Read; + rtc_index = -1; + return; + } + + if(data == 0x0e) { + rtc_mode = RTCM_Command; + return; + } + + if(data == 0x0f) return; //unknown behavior + + if(rtc_mode == RTCM_Write) { + if(rtc_index >= 0 && rtc_index < 12) { + memory::cartrtc.write(rtc_index++, data); + + if(rtc_index == 12) { + //day of week is automatically calculated and written + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8); + unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100; + year += 1000; + + memory::cartrtc.write(rtc_index++, weekday(year, month, day)); + } + } + } else if(rtc_mode == RTCM_Command) { + if(data == 0) { + rtc_mode = RTCM_Write; + rtc_index = 0; + } else if(data == 4) { + rtc_mode = RTCM_Ready; + rtc_index = -1; + for(unsigned i = 0; i < 13; i++) memory::cartrtc.write(i, 0); + } else { + //unknown behavior + rtc_mode = RTCM_Ready; + } + } + } +} + +SRTC::SRTC() { +} diff --git a/src/chip/srtc/srtc.h b/src/chip/srtc/srtc.h index 33531a7b..c0d9ab6f 100644 --- a/src/chip/srtc/srtc.h +++ b/src/chip/srtc/srtc.h @@ -1,52 +1,22 @@ -class SRTC : public MMIO { -public: -enum { MAX_SRTC_INDEX = 0x0c }; - -enum { - SRTC_READ = 0, - SRTC_WRITE, - SRTC_COMMAND, - SRTC_READY -}; - -enum { - SRTC_COMMAND_WRITE = 0, - SRTC_COMMAND_CLEAR = 4 -}; - -/****************************** -[srtc.data structure] -Index Description Range ------ ----------- ----- - 0 Seconds low 0-9 - 1 Seconds high 0-5 - 2 Minutes low 0-9 - 3 Minutes high 0-5 - 4 Hour low 0-9 - 5 Hour high 0-2 - 6 Day low 0-9 - 7 Day high 0-3 - 8 Month 1-12 - 9 Year ones 0-9 - 10 Year tens 0-9 - 11 Year hundreds 9-11 (9=19xx, 10=20xx, 11=21xx) - 12 Day of week 0-6 (0=Sunday, ...) -******************************/ -struct { - int8 index; - uint8 mode; - uint8 data[MAX_SRTC_INDEX + 1]; -} srtc; - void set_time(); - void init(); - void enable(); - void power(); - void reset(); - - uint8 mmio_read (uint addr); - void mmio_write(uint addr, uint8 data); - - SRTC(); -}; - -extern SRTC srtc; +class SRTC : public MMIO { +public: + void update_time(); + unsigned weekday(unsigned year, unsigned month, unsigned day); + + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read (uint addr); + void mmio_write(uint addr, uint8 data); + + SRTC(); + +private: + static const unsigned months[12]; + enum RTC_Mode { RTCM_Ready, RTCM_Command, RTCM_Read, RTCM_Write } rtc_mode; + signed rtc_index; +}; + +extern SRTC srtc; diff --git a/src/lib/hiro/cc.bat b/src/lib/hiro/cc.bat new file mode 100644 index 00000000..5947ec02 --- /dev/null +++ b/src/lib/hiro/cc.bat @@ -0,0 +1,6 @@ +mingw32-g++ -c test/test.cpp -I. -I../ +mingw32-g++ -c hiro.cpp -I. -I../ +mingw32-g++ -c ../nall/string.cpp -I. -I../ +mingw32-g++ test.o hiro.o string.o -o test_app.exe -lkernel32 -luser32 -lgdi32 -ladvapi32 -lcomctl32 -lcomdlg32 +@pause +@del *.o diff --git a/src/lib/hiro/cc.sh b/src/lib/hiro/cc.sh new file mode 100644 index 00000000..11d88958 --- /dev/null +++ b/src/lib/hiro/cc.sh @@ -0,0 +1,6 @@ +clear +g++ -c test/test.cpp -I. -I../ +g++ -c hiro.cpp `pkg-config --cflags gtk+-2.0` -I. -I../ +g++ -c ../nall/string.cpp -I. -I../ +g++ test.o hiro.o string.o -o test_app `pkg-config --libs gtk+-2.0` -lXtst +rm *.o diff --git a/src/lib/hiro/test/test.cpp b/src/lib/hiro/test/test.cpp new file mode 100644 index 00000000..41d17e0b --- /dev/null +++ b/src/lib/hiro/test/test.cpp @@ -0,0 +1,237 @@ +#include "../hiro.h" +using namespace libhiro; + +#include +using nall::min; +using nall::max; + +bool kill_ = false; + +uint32_t windowicon[16 * 16] = { + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, + 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, +}; + +class SubWindow : public Window { +public: + Button button; + + uintptr_t tick(Event) { + hide(); + } + + void setup() { + create(0, 595, 80); + + button.create(0, 595, 80, "SubWindow (click to hide)"); + button.on_tick = bind(&SubWindow::tick, this); + attach(button, 0, 0); + } +} subwindow; + +class MainWindow : public Window { +public: + MenuGroup menu_file; + MenuGroup menu_file_disk; + MenuItem menu_file_disk_load; + MenuItem menu_file_disk_save; + MenuSeparator menu_file_separator; + MenuItem menu_file_exit; + MenuGroup menu_help; + MenuCheckItem menu_help_check1, menu_help_check2; + MenuSeparator menu_help_separator1; + MenuRadioItem menu_help_radio1, menu_help_radio2, menu_help_radio3; + MenuSeparator menu_help_separator2; + MenuItem menu_help_about; + Label label; + Editbox editbox, editbox_multi; + Button button_ok; + Button button_exit; + Checkbox check1, check2; + Radiobox radio1, radio2; + Progressbar progress; + Combobox combobox; + Listbox listbox; + Slider hslider, vslider; + Frame frame; + Canvas canvas; + + uintptr_t change(Event e) { + printf("change(%d)\n", (uint)e.param); + } + + uintptr_t activate(Event e) { + printf("activate(%d)\n", (uint)e.param); + } + + uintptr_t tick(Event e) { + printf("tick(%d)\n", e.param); + if(e.widget == &button_ok) { + char t[4096]; + editbox.get_text(t, 4096); + printf("'%s'\n", t); + } + if(e.widget == &menu_file_disk_load) { + char t[4096] = ""; + hiro().file_open(0, t, "", "Source files\t*.cpp,*.h\nAll Files\t*.*"); + printf("'%s'\n", t); + } + if(e.widget == &menu_file_disk_save) { + char t[4096] = ""; + hiro().file_save(0, t, "", "Source files\t*.cpp,*.h\nAll Files\t*.*"); + printf("'%s'\n", t); + } + } + + uintptr_t keydown(Event e) { + static bool fs = false; + if(e.param == nall::keyboard::f11) { + fs = !fs; + fs ? fullscreen() : unfullscreen(); + printf("%d -> %4d, %4d\n", fs, get_width(), get_height()); + } else if(e.param == nall::keyboard::escape) { + menu.show(!menu.visible()); + } + } + + uintptr_t close(Event) { + printf("close()\n"); + return kill_ = true; + } + + void setup() { + create(Window::AutoCenter, 605, 320, "hiro test application"); + //set_opacity(224); + //set_background_color(0, 0, 0); + set_icon(16, 16, windowicon); + + attach(menu_file.create("File")); + menu_file.attach(menu_file_disk.create("Disk")); + menu_file_disk.attach(menu_file_disk_load.create("Load ...")); + menu_file_disk.attach(menu_file_disk_save.create("Save ...")); + menu_file.attach(menu_file_separator.create()); + menu_file.attach(menu_file_exit.create("Exit")); + attach(menu_help.create("Help")); + menu_help.attach(menu_help_check1.create("Check 1")); + menu_help.attach(menu_help_check2.create("Check 2")); + menu_help.attach(menu_help_separator1.create()); + { MenuRadioItemGroup group; + group.add(&menu_help_radio1); + group.add(&menu_help_radio2); + group.add(&menu_help_radio3); + menu_help.attach(menu_help_radio1.create(group, "Radio 1")); + menu_help.attach(menu_help_radio2.create(group, "Radio 2")); + menu_help.attach(menu_help_radio3.create(group, "Radio 3")); + } menu_help.attach(menu_help_separator2.create()); + menu_help.attach(menu_help_about.create("About ...")); + menu_help_about.disable(); + label.create(0, 200, 35, "hiro test application\n~ byuu"); + editbox.create(0, 200, 25); + button_ok.create(0, 100, 30, "Ok"); + button_exit.create(0, 100, 30, "Exit"); + editbox_multi.create(Editbox::Multiline | Editbox::VerticalScrollAlways, 200, 95); + check1.create(0, 100, 20, "Check 1"); + check2.create(0, 100, 20, "Check 2"); + { RadioboxGroup group; + group.add(&radio1); + group.add(&radio2); + radio1.create(group, 0, 100, 20, "Radio 1"); + radio2.create(group, 0, 100, 20, "Radio 2"); + } progress.create(0, 200, 30); + progress.set_progress(50); + combobox.create(0, 200, 30); + combobox.add_item("Option 1"); + combobox.add_item("Option 2"); + combobox.add_item("Option 3"); + listbox.create(Listbox::Header | Listbox::VerticalScrollAlways, 200, 100, "Name\tValue"); + listbox.add_item("a\ttrue"); + listbox.add_item("b\tfalse"); + hslider.create(Slider::Horizontal, 425, 25, 10); + vslider.create(Slider::Vertical, 25, 200, 10); + frame.create(0, 155, 225, "Canvas:"); + + canvas.create(0, 135, 195); + for(uint y = 0; y < 195; y++) { + uint32_t *p = canvas.buffer() + y * 135; + for(uint x = 0; x < 135; x++) { + double dx = 128.0 / 135.0 * double(x); + double dy = 128.0 / 195.0 * double(y); + uint32_t c = uint32_t(dx) + uint32_t(dy); + *p++ = (max(0U, min(c, 255U)) ^ 0xff) << 16; + } + } + + status.show(); + status.set_text("Statusbar"); + + on_close = bind(&MainWindow::close, this); + on_keydown = bind(&MainWindow::keydown, this); + + menu_file_disk_load.on_tick = + menu_file_disk_save.on_tick = bind(&MainWindow::tick, this); + menu_file_exit.on_tick = on_close; + menu_help_check1.on_tick = menu_help_check2.on_tick = + menu_help_radio1.on_tick = menu_help_radio2.on_tick = + menu_help_radio3.on_tick = bind(&MainWindow::tick, this); + menu_help_about.on_tick = bind(&MainWindow::tick, this); + button_ok.on_tick = bind(&MainWindow::tick, this); + button_exit.on_tick = bind(&MainWindow::close, this); + check1.on_tick = check2.on_tick = + radio1.on_tick = radio2.on_tick = bind(&MainWindow::tick, this); + combobox.on_change = bind(&MainWindow::change, this); + listbox.on_change = bind(&MainWindow::change, this); + listbox.on_activate = bind(&MainWindow::activate, this); + hslider.on_change = bind(&MainWindow::change, this); + vslider.on_change = bind(&MainWindow::change, this); + + attach(label, 5, 5); + attach(editbox, 5, 40); + attach(button_ok, 5, 70); + attach(button_exit, 105, 70); + attach(editbox_multi, 210, 5); + attach(check1, 5, 105); + attach(check2, 105, 105); + attach(radio1, 5, 125); + attach(radio2, 105, 125); + attach(progress, 5, 145); + attach(combobox, 5, 175); + attach(listbox, 210, 105); + attach(hslider, 5, 205); + attach(vslider, 415, 5); + + attach(frame, 445, 5); + attach(canvas, 455, 25); + + attach(subwindow, 5, 235); + } +} window; + +int main() { + hiro().init(); + hiro().disable_screensaver(); + + subwindow.setup(); + window.setup(); + window.show(); + + window.check1.check(); + + while(kill_ == false) hiro().run(); + + hiro().term(); + return 0; +} diff --git a/src/lib/hiro/win/canvas.cpp b/src/lib/hiro/win/canvas.cpp index b0755500..6780a0d0 100644 --- a/src/lib/hiro/win/canvas.cpp +++ b/src/lib/hiro/win/canvas.cpp @@ -8,6 +8,11 @@ void pCanvas::create(uint style, uint width, uint height) { } void pCanvas::redraw() { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + SetDIBitsToDevice(ps.hdc, 0, 0, iwidth, iheight, 0, 0, 0, iheight, (void*)ibuffer, &bmi, DIB_RGB_COLORS); + EndPaint(hwnd, &ps); + InvalidateRect(hwnd, 0, FALSE); } uint32_t* pCanvas::buffer() { @@ -30,13 +35,6 @@ pCanvas::~pCanvas() { /* internal */ -void pCanvas::blit() { - PAINTSTRUCT ps; - BeginPaint(hwnd, &ps); - SetDIBitsToDevice(ps.hdc, 0, 0, iwidth, iheight, 0, 0, 0, iheight, (void*)ibuffer, &bmi, DIB_RGB_COLORS); - EndPaint(hwnd, &ps); -} - void pCanvas::resize(uint width, uint height) { if(ibuffer) free(ibuffer); diff --git a/src/lib/hiro/win/canvas.h b/src/lib/hiro/win/canvas.h index 2bc478ff..71318e7c 100644 --- a/src/lib/hiro/win/canvas.h +++ b/src/lib/hiro/win/canvas.h @@ -12,6 +12,5 @@ public: BITMAPINFO bmi; uint32_t *ibuffer; uint ipitch, iwidth, iheight; - void blit(); void resize(uint width, uint height); }; diff --git a/src/lib/hiro/win/hiro.cpp b/src/lib/hiro/win/hiro.cpp index d0c18cf0..ebf27bef 100644 --- a/src/lib/hiro/win/hiro.cpp +++ b/src/lib/hiro/win/hiro.cpp @@ -312,7 +312,7 @@ LRESULT pHiro::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } break; case WM_PAINT: { - if(p && p->self.type == Widget::CanvasType) ((pCanvas*)p)->blit(); + if(p && p->self.type == Widget::CanvasType) ((pCanvas*)p)->redraw(); } break; case WM_COMMAND: { diff --git a/src/lib/hiro/win/keymap.cpp b/src/lib/hiro/win/keymap.cpp index 5437f5bb..8dfafedc 100644 --- a/src/lib/hiro/win/keymap.cpp +++ b/src/lib/hiro/win/keymap.cpp @@ -30,6 +30,13 @@ uint16_t pHiro::translate_key(uint key) { case '8': return keyboard::num_8; case '9': return keyboard::num_9; + case VK_INSERT: return keyboard::insert; + case VK_DELETE: return keyboard::delete_; + case VK_HOME: return keyboard::home; + case VK_END: return keyboard::end; + case VK_PRIOR: return keyboard::page_up; + case VK_NEXT: return keyboard::page_down; + case 'A': return keyboard::a; case 'B': return keyboard::b; case 'C': return keyboard::c; diff --git a/src/lib/ruby/audio/alsa.cpp b/src/lib/ruby/audio/alsa.cpp index bf988eff..f397e833 100644 --- a/src/lib/ruby/audio/alsa.cpp +++ b/src/lib/ruby/audio/alsa.cpp @@ -13,15 +13,16 @@ public: struct { snd_pcm_t *handle; snd_pcm_format_t format; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; int channels; const char *name; unsigned latency; } device; struct { - uint16_t *data; + uint32_t *data; unsigned length; - unsigned size; } buffer; struct { @@ -53,30 +54,27 @@ public: void sample(uint16_t left, uint16_t right) { if(!device.handle) return; - buffer.data[buffer.length++] = left; - buffer.data[buffer.length++] = right; - if(buffer.length + 2 < buffer.size) return; //will crash in some cases if not stopped two before - - snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer.data, buffer.length); - if(written < 0) { - snd_pcm_recover(device.handle, written, 1); - //no samples written, drop one sample to prevent possible emulation stall - buffer.length -= 2; - memmove(buffer.data, buffer.data + 2, buffer.length * sizeof(uint16_t)); - } else if(written < buffer.length) { - //only some samples written - buffer.length -= written; - memmove(buffer.data, buffer.data + written, buffer.length * sizeof(uint16_t)); - } else { - //all samples written - buffer.length = 0; - } + buffer.data[buffer.length++] = left + (right << 16); + if(buffer.length < device.period_size) return; + uint32_t *buffer_ptr = buffer.data; + do { + snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); + if(written < 0) { + //no samples written + snd_pcm_recover(device.handle, written, 1); + } else if(written < buffer.length) { + //only some samples written + buffer.length -= written; + buffer_ptr += written; + } else { + //all samples written + buffer.length = 0; + } + } while(buffer.length > 0); } bool init() { - buffer.data = new uint16_t[buffer.size]; - - if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { + if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { //failed to initialize term(); return false; @@ -89,6 +87,11 @@ public: return false; } + if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) { + device.period_size = device.latency * 1e-6 * settings.frequency / 4; + } + + buffer.data = new uint32_t[device.period_size]; return true; } @@ -110,11 +113,10 @@ public: device.format = SND_PCM_FORMAT_S16_LE; device.channels = 2; device.name = "default"; - device.latency = 90; + device.latency = 100000; buffer.data = 0; buffer.length = 0; - buffer.size = device.latency * 32; settings.frequency = 22050; } diff --git a/src/lib/ruby/audio/alsa.h b/src/lib/ruby/audio/alsa.h index b6ca8509..d1304dc9 100644 --- a/src/lib/ruby/audio/alsa.h +++ b/src/lib/ruby/audio/alsa.h @@ -1,6 +1,6 @@ /* - audio.alsa (2008-05-04) - author: Nach + audio.alsa (2008-06-01) + authors: Nach, RedDwarf */ class pAudioALSA; diff --git a/src/lib/ruby/audio/ao.cpp b/src/lib/ruby/audio/ao.cpp index 9c47dc5e..e390f468 100644 --- a/src/lib/ruby/audio/ao.cpp +++ b/src/lib/ruby/audio/ao.cpp @@ -47,16 +47,23 @@ public: bool init() { driver_id = ao_default_driver_id(); //ao_driver_id((const char*)driver) - if(driver_id < 0) driver_id = ao_default_driver_id(); //fallback on default if driver doesn't exist + if(driver_id < 0) return false; + driver_format.bits = 16; driver_format.channels = 2; driver_format.rate = settings.frequency; driver_format.byte_format = AO_FMT_LITTLE; - audio_device = ao_open_live(driver_id, &driver_format, 0); + ao_option *options = 0; + ao_info *di = ao_driver_info(driver_id); + if(!di) return false; + if(!strcmp(di->short_name, "alsa")) { + ao_append_option(&options, "buffer_time", "100000"); //100ms latency (default was 500ms) + } + + audio_device = ao_open_live(driver_id, &driver_format, options); if(!audio_device) return false; - ao_info *di = ao_driver_info(driver_id); return true; } diff --git a/src/lib/ruby/audio/ao.h b/src/lib/ruby/audio/ao.h index c343e419..adfea530 100644 --- a/src/lib/ruby/audio/ao.h +++ b/src/lib/ruby/audio/ao.h @@ -1,6 +1,6 @@ /* - audio.ao (2007-12-26) - author: Nach + audio.ao (2008-06-01) + authors: Nach, RedDwarf */ class pAudioAO; diff --git a/src/lib/sync.sh b/src/lib/sync.sh new file mode 100644 index 00000000..f8ecd2a7 --- /dev/null +++ b/src/lib/sync.sh @@ -0,0 +1,6 @@ +rm -r nall +rm -r hiro +rm -r ruby +cp -r ../../../nall ./nall +cp -r ../../../hiro ./hiro +cp -r ../../../ruby ./ruby diff --git a/src/memory/smemory/mapper/generic.cpp b/src/memory/smemory/mapper/generic.cpp index 30eb7665..48c61468 100644 --- a/src/memory/smemory/mapper/generic.cpp +++ b/src/memory/smemory/mapper/generic.cpp @@ -32,9 +32,18 @@ void sBus::map_generic() { map_generic_sram(); } break; + case Cartridge::SPC7110ROM: { + map(MapDirect, 0x00, 0x00, 0x6000, 0x7fff, spc7110); + map(MapDirect, 0x00, 0x0f, 0x8000, 0xffff, spc7110); + map(MapDirect, 0x30, 0x30, 0x6000, 0x7fff, spc7110); + map(MapDirect, 0x50, 0x50, 0x0000, 0xffff, spc7110); + map(MapDirect, 0x80, 0x8f, 0x8000, 0xffff, spc7110); + map(MapDirect, 0xc0, 0xff, 0x0000, 0xffff, spc7110); + } break; + case Cartridge::BSXROM: { - //full map is dynamically mapped by: - //src/chip/bsx/bsx_cart.cpp : BSXCart::update_memory_map(); + //full map is dynamically mapped by: + //src/chip/bsx/bsx_cart.cpp : BSXCart::update_memory_map(); map(MapLinear, 0x00, 0x3f, 0x8000, 0xffff, memory::cartrom); map(MapLinear, 0x80, 0xbf, 0x8000, 0xffff, memory::cartrom); } break; diff --git a/src/snes/snes.cpp b/src/snes/snes.cpp index 4cd7f5b8..9d2f82d5 100644 --- a/src/snes/snes.cpp +++ b/src/snes/snes.cpp @@ -7,6 +7,7 @@ BSXCart bsxcart; BSXFlash bsxflash; SRTC srtc; SDD1 sdd1; +SPC7110 spc7110; Cx4 cx4; DSP1 dsp1; DSP2 dsp2; @@ -34,6 +35,7 @@ void SNES::init() { bsxflash.init(); srtc.init(); sdd1.init(); + spc7110.init(); cx4.init(); dsp1.init(); dsp2.init(); @@ -67,6 +69,7 @@ void SNES::power() { if(cartridge.info.bsxflash) bsxflash.power(); if(cartridge.info.srtc) srtc.power(); if(cartridge.info.sdd1) sdd1.power(); + if(cartridge.info.spc7110) spc7110.power(); if(cartridge.info.cx4) cx4.power(); if(cartridge.info.dsp1) dsp1.power(); if(cartridge.info.dsp2) dsp2.power(); @@ -87,6 +90,7 @@ void SNES::power() { if(cartridge.info.bsxflash) bsxflash.enable(); if(cartridge.info.srtc) srtc.enable(); if(cartridge.info.sdd1) sdd1.enable(); + if(cartridge.info.spc7110) spc7110.enable(); if(cartridge.info.cx4) cx4.enable(); if(cartridge.info.dsp1) dsp1.enable(); if(cartridge.info.dsp2) dsp2.enable(); @@ -112,6 +116,7 @@ void SNES::reset() { if(cartridge.info.bsxflash) bsxflash.reset(); if(cartridge.info.srtc) srtc.reset(); if(cartridge.info.sdd1) sdd1.reset(); + if(cartridge.info.spc7110) spc7110.reset(); if(cartridge.info.cx4) cx4.reset(); if(cartridge.info.dsp1) dsp1.reset(); if(cartridge.info.dsp2) dsp2.reset(); diff --git a/src/ui/base/about.cpp b/src/ui/base/about.cpp index c020f69c..84d9ac1f 100644 --- a/src/ui/base/about.cpp +++ b/src/ui/base/about.cpp @@ -4,7 +4,7 @@ uintptr_t AboutWindow::close(Event) { } void AboutWindow::setup() { - create(Window::AutoCenter, 320, 130, translate["About bsnes ..."]); + create(Window::AutoCenter, 360, 135, translate["About bsnes ..."]); set_icon(48, 48, (uint32_t*)resource::icon48); icon.create(0, 48, 48); @@ -13,10 +13,10 @@ void AboutWindow::setup() { << translate["Author"] << ": byuu\n" << translate["Project began: October 14th, 2004"] ); - contributors.create(0, 275, 70, string() + contributors.create(0, 350, 75, string() << translate["Contributors:"] << "\n" - << " anomie, blargg, DMV27, GIGO, kode54, Nach,\n" - << " Overload, Richard Bannister, TRAC, zones\n" + << " Andreas Naive, anomie, blargg, DMV27, GIGO, kode54,\n" + << " neviksti, Nach, Overload, Richard Bannister, TRAC, zones\n" << "\n" << translate["Localization by: byuu"] ); diff --git a/src/ui/event.cpp b/src/ui/event.cpp index effab6f8..d41eefe6 100644 --- a/src/ui/event.cpp +++ b/src/ui/event.cpp @@ -318,7 +318,6 @@ void load_cart_normal(const char *filename) { const char *name; if(cartridge.info.superfx) { name = "SuperFX"; replace(message, "$", name); alert(message); } if(cartridge.info.sa1) { name = "SA-1"; replace(message, "$", name); alert(message); } - if(cartridge.info.spc7110) { name = "SPC7110"; replace(message, "$", name); alert(message); } if(cartridge.info.st011) { name = "ST011"; replace(message, "$", name); alert(message); } if(cartridge.info.st018) { name = "ST018"; replace(message, "$", name); alert(message); }