diff -NaHudr b/src/Makefile.am a/src/Makefile.am --- b/src/Makefile.am 2005-04-13 13:54:30.000000000 +0300 +++ a/src/Makefile.am 2004-04-17 04:22:15.000000000 +0300 @@ -24,6 +24,5 @@ endif fceu_SOURCES += $(fceud_SOURCES) -fceu_LIBS = $(fceud_LIBS) DEFAULT_INCLUDES = diff -NaHudr b/src/drawing.h a/src/drawing.h --- b/src/drawing.h 2005-04-13 13:54:26.000000000 +0300 +++ a/src/drawing.h 2005-04-13 14:00:16.000000000 +0300 @@ -112,117 +112,3 @@ } } -static uint8 play_slines[]= -{ - 0, 0, 1, - 1, 0, 2, - 2, 0, 3, - 3, 0, 4, - 4, 0, 5, - 5, 0, 6, - 6, 0, 7, - 7, 0, 8, - 8, 0, 7, - 9, 0, 6, - 10, 0, 5, - 11, 0, 4, - 12, 0, 3, - 13, 0, 2, - 14, 0, 1, - 99, -}; - -static uint8 record_slines[]= -{ - 0, 5, 9, - 1, 3, 11, - 2, 2, 12, - 3, 1, 13, - 4, 1, 13, - 5, 0, 14, - 6, 0, 14, - 7, 0, 14, - 8, 0, 14, - 9, 0, 14, - 10, 1, 13, - 11, 1, 13, - 12, 2, 12, - 13, 3, 11, - 14, 5, 9, - 99, -}; - -static uint8 pause_slines[]= -{ - 0, 2, 6, - 1, 2, 6, - 2, 2, 6, - 3, 2, 6, - 4, 2, 6, - 5, 2, 6, - 6, 2, 6, - 7, 2, 6, - 8, 2, 6, - 9, 2, 6, - 10, 2, 6, - 11, 2, 6, - 12, 2, 6, - 13, 2, 6, - 14, 2, 6, - - 0, 9, 13, - 1, 9, 13, - 2, 9, 13, - 3, 9, 13, - 4, 9, 13, - 5, 9, 13, - 6, 9, 13, - 7, 9, 13, - 8, 9, 13, - 9, 9, 13, - 10, 9, 13, - 11, 9, 13, - 12, 9, 13, - 13, 9, 13, - 14, 9, 13, - 99, -}; - -static uint8 no_slines[]= -{ - 99 -}; - -static uint8* sline_icons[4]= -{ - no_slines, - play_slines, - record_slines, - pause_slines -}; - -void FCEU_DrawRecordingStatusN(uint8* XBuf, int n) -{ - uint8* slines=sline_icons[n]; - int i; - - XBuf += (FSettings.LastSLine-28)*256 + 240 + 255; - for(i=0; slines[i]!=99; i+=3) - { - int y=slines[i]; - uint8* dest=XBuf+(y*256); - int x; - for(x=slines[i+1]; x!=slines[i+2]; ++x) - dest[x]=0; - } - - XBuf -= 255; - for(i=0; slines[i]!=99; i+=3) - { - int y=slines[i]; - uint8* dest=XBuf+(y*256); - int x; - for(x=slines[i+1]; x!=slines[i+2]; ++x) - dest[x]=4; - } -} diff -NaHudr b/src/drivers/pc/Makefile.am.inc a/src/drivers/pc/Makefile.am.inc --- b/src/drivers/pc/Makefile.am.inc 2005-04-13 13:54:26.000000000 +0300 +++ a/src/drivers/pc/Makefile.am.inc 2005-04-07 15:00:37.000000000 +0300 @@ -1,4 +1,4 @@ -fceud_SOURCES = drivers/pc/input.c drivers/pc/main.c drivers/pc/sdl.c drivers/pc/sdl-joystick.c drivers/pc/sdl-sound.c drivers/pc/sdl-throttle.c drivers/pc/sdl-video.c drivers/pc/unix-netplay.c +fceud_SOURCES = drivers/pc/input.c drivers/pc/main.c drivers/pc/sdl.c drivers/pc/sdl-joystick.c drivers/pc/sdl-sound.c drivers/pc/sdl-throttle.c drivers/pc/sdl-video.c drivers/pc/unix-netplay.c drivers/pc/tiletracker.cc if OPENGL TMP_OGL = drivers/pc/sdl-opengl.c diff -NaHudr b/src/drivers/pc/doubledeque.hh a/src/drivers/pc/doubledeque.hh --- b/src/drivers/pc/doubledeque.hh 1970-01-01 02:00:00.000000000 +0200 +++ a/src/drivers/pc/doubledeque.hh 2005-04-08 22:46:23.000000000 +0300 @@ -0,0 +1,203 @@ +//************************************************************************ +// DoubleDeque +//************************************************************************ + + +#ifndef DOUBLEDEQUE_HH +#define DOUBLEDEQUE_HH + +#include + +template +class DoubleDeque +{ + public: + class Row; + + ItemType& at(int rowIndex, int colIndex); + + const Row& getRow(int rowIndex) const; + + void clear(); + + + DoubleDeque() {} + + +//----------------------------------------------------------------------- + private: + template + class AutoDeque; + + AutoDeque rows; + + // Disable copying and assignment: + DoubleDeque(const DoubleDeque&); + const DoubleDeque& operator=(const DoubleDeque&); +}; + + +template +class DoubleDeque::Row +{ + public: + ItemType& at(int colIndex); + const ItemType& at(int colIndex) const; + + + private: + AutoDeque data; +}; + + + +//======================================================================= +//======================================================================= +// Implementations +//======================================================================= +//======================================================================= + +template +template +class DoubleDeque::AutoDeque +{ + static const int BLOCK_SIZE = 65536; + + std::vector dataBlocks; + int currentMinIndex, currentMaxIndex; + int firstItemAbsoluteIndex; + + T defaultValue; + + int getBlockIndexOf(int index) + { + if(index >= 0) return index/BLOCK_SIZE; + return (index-(BLOCK_SIZE-1))/BLOCK_SIZE; + } + + + public: + AutoDeque(): + currentMinIndex(0), currentMaxIndex(-1), firstItemAbsoluteIndex(0) + {} + + ~AutoDeque() + { + clear(); + } + + T& at(int index) + { + if(dataBlocks.empty()) + { + dataBlocks.push_back(new T[BLOCK_SIZE]); + currentMinIndex = index; + currentMaxIndex = index; + firstItemAbsoluteIndex = + index - BLOCK_SIZE*getBlockIndexOf(index); + } + + else if(index < currentMinIndex) + { + const int currentMinIndexBlock = getBlockIndexOf(currentMinIndex); + const int newMinIndexBlock = getBlockIndexOf(index); + if(newMinIndexBlock < currentMinIndexBlock) + { + const unsigned newBlocksAmount = + currentMinIndexBlock - newMinIndexBlock; + + // We assume this happens rarely so its speed doesn't + // really matter: + dataBlocks.insert(dataBlocks.begin(), newBlocksAmount, 0); + + for(unsigned i = 0; i < newBlocksAmount; ++i) + { + dataBlocks[i] = new T[BLOCK_SIZE]; + } + } + + currentMinIndex = index; + firstItemAbsoluteIndex = index - BLOCK_SIZE*newMinIndexBlock; + } + + else if(index > currentMaxIndex) + { + const int currentMaxIndexBlock = getBlockIndexOf(currentMaxIndex); + const int newMaxIndexBlock = getBlockIndexOf(index); + if(newMaxIndexBlock > currentMaxIndexBlock) + { + const unsigned newBlocksAmount = + newMaxIndexBlock - currentMaxIndexBlock; + + for(unsigned i = 0; i < newBlocksAmount; ++i) + { + dataBlocks.push_back(new T[BLOCK_SIZE]); + } + } + + currentMaxIndex = index; + } + + const int i = index - currentMinIndex + firstItemAbsoluteIndex; + return dataBlocks[i/BLOCK_SIZE][i%BLOCK_SIZE]; + } + + const T& at(int index) const + { + if(index < currentMinIndex || index > currentMaxIndex) + return defaultValue; + + const int i = index - currentMinIndex + firstItemAbsoluteIndex; + return dataBlocks[i/BLOCK_SIZE][i%BLOCK_SIZE]; + } + + void clear() + { + currentMinIndex = 0; currentMaxIndex = -1; + for(unsigned i = 0; i < dataBlocks.size(); ++i) + delete[] dataBlocks[i]; + dataBlocks.clear(); + } + + + private: + // Disable copying and assignment: + AutoDeque(const AutoDeque&); + const AutoDeque& operator=(const AutoDeque&); +}; + + +template +ItemType& DoubleDeque::Row::at(int colIndex) +{ + return data.at(colIndex); +} + +template +const ItemType& DoubleDeque::Row::at(int colIndex) const +{ + return data.at(colIndex); +} + +template +ItemType& DoubleDeque::at(int rowIndex, int colIndex) +{ + return rows.at(rowIndex).at(colIndex); +} + +template +const typename DoubleDeque::Row& +DoubleDeque::getRow(int rowIndex) const +{ + return rows.at(rowIndex); +} + +template +void DoubleDeque::clear() +{ + rows.clear(); +} + + + +#endif diff -NaHudr b/src/drivers/pc/hash.hh a/src/drivers/pc/hash.hh --- b/src/drivers/pc/hash.hh 1970-01-01 02:00:00.000000000 +0200 +++ a/src/drivers/pc/hash.hh 2004-06-11 15:38:22.000000000 +0300 @@ -0,0 +1,87 @@ +#ifndef bqtHashHH +#define bqtHashHH + +/* Set to 0 if you have compilation problems + * with hash_set or hash_map + */ +#define USE_HASH 1 + + +#if USE_HASH + +#include + +#include +#include +using namespace __gnu_cxx; + +using std::basic_string; + +struct BitSwapHashFon +{ + size_t operator() (unsigned n) const + { + unsigned rot = n&31; + return (n << rot) | (n >> (32-rot)); + } +}; + +namespace __gnu_cxx +{ + template + struct hash > + { + size_t operator() (const basic_string &s) const + { + unsigned h=0; + for(unsigned a=0,b=s.size(); a + { + size_t operator() (const std::wstring &s) const + { + unsigned h=0; + for(unsigned a=0,b=s.size(); a + { + size_t operator() (wchar_t n) const + { + /* Since values of n<128 are the most common, + * values of n<256 the second common + * and big values of n are rare, we rotate some + * bits to make the distribution more even. + * Multiplying n by 33818641 (a prime near 2^32/127) scales + * the numbers nicely to fit the whole range and keeps the + * distribution about even. + */ + return (n * 33818641UL); + } + }; +} + +#else + +#include +#include + +#define hash_map std::map +#define hash_set std::set + +#endif // USE_HASH + +#endif // bqtHashHH diff -NaHudr b/src/drivers/pc/input.c a/src/drivers/pc/input.c --- b/src/drivers/pc/input.c 2005-04-13 13:55:02.000000000 +0300 +++ a/src/drivers/pc/input.c 2005-04-12 20:54:20.000000000 +0300 @@ -127,9 +127,16 @@ static int cidisabled=0; +static const char* WillLoadMovie = NULL; static void KeyboardCommands(void) { - int is_shift, is_alt; + int is_shift, is_alt, is_ctrl; + + if(WillLoadMovie) + { + FCEUI_LoadMovie(WillLoadMovie, 1); + WillLoadMovie = NULL; + } keys=GetKeyboard(); @@ -148,15 +155,18 @@ is_shift = KEY(LEFTSHIFT) | KEY(RIGHTSHIFT); is_alt = KEY(LEFTALT) | KEY(RIGHTALT); + is_ctrl = KEY(LEFTCONTROL) | KEY(RIGHTCONTROL); if(keyonly(F4)) { if(is_shift) FCEUI_SetRenderDisable(-1, 2); else FCEUI_SetRenderDisable(2, -1); } +#if 0 #ifdef SDL if(keyonly(ENTER) && is_alt) ToggleFS(); #endif +#endif NoWaiting&=~1; if(KEY(GRAVE)) @@ -175,23 +185,37 @@ if(keyonly(F2)) DoCheatSeq(); #endif - if(keyonly(1) && is_shift) + if(keyonly(1)) { + if(is_shift) + { char Buf[4096]; - fprintf(stderr, "Which file to create? (empty to cancel): "); fflush(stderr); + fprintf(stderr, "Which movie file to create? (empty to cancel): "); fflush(stderr); fgets(Buf, sizeof Buf, stdin); strtok(Buf, "\r\n"); if(*Buf) { - FCEUI_SaveMovie(Buf, 0, NULL); + char Buf2[16]; + int flag = 0; + fprintf(stderr, "From poweron? (Y/N, default Y): "); fflush(stderr); + fgets(Buf2, sizeof Buf2, stdin); + strtok(Buf2, "\r\n"); + if(tolower(*Buf2) != 'n') flag |= MOVIE_FLAG_FROM_RESET; + + FCEUI_SaveMovie(Buf, flag, NULL); } else fprintf(stderr, "Cancelled\n"); + } + else + keyonce[MKK(1)] = 0; } - if(keyonly(2) && is_shift) + if(keyonly(2)) { + if(is_shift) + { char Buf[4096]; - fprintf(stderr, "Which file to load? (empty to cancel): "); fflush(stderr); + fprintf(stderr, "Which movie file to load? (empty to cancel): "); fflush(stderr); fgets(Buf, sizeof Buf, stdin); strtok(Buf, "\r\n"); if(*Buf) @@ -200,29 +224,37 @@ } else fprintf(stderr, "Cancelled\n"); + } + else + keyonce[MKK(2)] = 0; } - if(keyonly(3) && is_shift) + if(keyonly(3)) { - FCEUI_StopMovie(); + if(is_shift) + FCEUI_StopMovie(); + else + keyonce[MKK(3)] = 0; } if(keyonly(F5) || keyonly(S)) { FCEUI_SaveState(NULL); } +/**/ if(keyonly(F7) || keyonly(F)) { FCEUI_LoadState(NULL); } +/**/ } if(keyonly(F1)) FCEUI_ToggleTileView(); - if(keyonly(MINUS)) DecreaseEmulationSpeed(); - if(keyonly(EQUAL)) IncreaseEmulationSpeed(); + if(keyonly(KP_MINUS) || keyonly(N)) DecreaseEmulationSpeed(); + if(keyonly(KP_PLUS) || keyonly(M)) IncreaseEmulationSpeed(); if(keyonly(BACKSPACE)) FCEUI_MovieToggleFrameDisplay(); - if(keyonly(BACKSLASH)) FCEUI_ToggleEmulationPause(); - if(keyonly(RIGHTCONTROL)) FCEUI_FrameAdvance(); + if(keyonly(ENTER)) FCEUI_ToggleEmulationPause(); + if(keyonly(QUOTE)) FCEUI_FrameAdvance(); if(keyonly(F10)) FCEUI_ResetNES(); if(keyonly(F11)) FCEUI_PowerNES(); @@ -235,6 +267,7 @@ if(KEY(F12) || KEY(ESCAPE)) CloseGame(); #endif +#if 0 if(gametype==GIT_VSUNI) { if(keyonly(F8)) FCEUI_VSUniCoin(); @@ -254,6 +287,7 @@ if(keyonly(8)) FCEUI_VSUniToggleDIP(7); } else +#endif { static uint8 bbuf[32]; static int bbuft; @@ -291,6 +325,8 @@ } } DIPSless: + if(is_alt && !is_shift && !is_ctrl) + { if(keyonly(0)) SSM(0); if(keyonly(1)) SSM(1); if(keyonly(2)) SSM(2); @@ -301,6 +337,7 @@ if(keyonly(7)) SSM(7); if(keyonly(8)) SSM(8); if(keyonly(9)) SSM(9); + } #undef SSM } } @@ -315,11 +352,11 @@ ButtConfig GamePadConfig[4][10]={ /* Gamepad 1 */ { - MK(KP3),MK(KP2), - MK(TAB),MK(ENTER), + MK(D),MK(A), + MK(H),MK(P), - MK(W),MK(Z), - MK(A),MK(S), + MK(CURSORUP),MK(CURSORDOWN), + MK(CURSORLEFT),MK(CURSORRIGHT), /* arrows */ MKZ(), MKZ() @@ -336,6 +373,48 @@ }; +#include +static __inline__ unsigned char inportb(const unsigned short port) +{ + unsigned char _v; + __asm__ volatile ("inb %1,%0" + :"=a" (_v) + :"d" ((unsigned short)port)); + return _v; +} +static __inline__ void outportb(const unsigned short port, + const unsigned char value) +{ + __asm__ volatile ("outb %0,%1" + ::"a" ((char) value),"d" ((unsigned short) port)); +} +static unsigned char ReadLPTport() +{ +return 0; + static unsigned lptport=0x278; + unsigned a; + unsigned char ret = 0x00; + struct timespec delay; + + outportb(lptport,15); + delay.tv_sec = 0; + delay.tv_nsec = 1000; + + for(a=0; a<8; a++) + { + register int b; + + outportb(lptport, 3); + outportb(lptport, 1); + + b = ((signed char)inportb(lptport+1)) < 0; + ret |= (b << a); + + nanosleep(&delay, NULL); + } + return ret; +} + static void UpdateGamepad(void) { static int rapid=0; @@ -356,13 +435,20 @@ if(DTestButton(&GamePadConfig[wg][8+x])) JS|=(1<0) BlitScreen(XBuf); + ++a; + //if(a > 460) _exit(0); + return; +/**/ + #ifdef NETWORK extern int FCEUDnetplay; #endif - int ocount = Count; - // apply frame scaling to Count - Count = (Count<<8)/fps_scale; - if(Count) + int32* buf = NULL; + int left = 0; + for(;;) { - int32 can=GetWriteSound(); - static int uflow=0; - int32 tmpcan; - - // don't underflow when scaling fps - if(can >= GetMaxSound() && fps_scale<=256) uflow=1; /* Go into massive underflow mode. */ - - if(can > Count) can=Count; - else uflow=0; - - WriteSound(Buffer,can); - - //if(uflow) puts("Underflow"); - tmpcan = GetWriteSound(); - // don't underflow when scaling fps - if(fps_scale>256 || ((tmpcan < Count*0.90) && !uflow)) - { - if(XBuf && (inited&4) && !(NoWaiting & 2)) - BlitScreen(XBuf); - Buffer+=can; - Count-=can; - if(Count) + int doing, can; +// moreaudio: + if(!left) { - if(NoWaiting) - { - can=GetWriteSound(); - if(Count>can) Count=can; - WriteSound(Buffer,Count); - } - else - { - while(Count>0) - { - WriteSound(Buffer,(Count= (Count * 1.8))) - { - if(Count > tmpcan) Count=tmpcan; - while(tmpcan > 0) + doing=left; + can=0; + if(Count > 0) can = GetWriteSound(); + + if(doing>can) doing=can; + //fprintf(stderr, "left=%d,Count=%d, can=%d,doing=%d\n", left,Count,can,doing); + if(doing > 0) WriteSound(buf,doing); + buf += doing; + left -= doing; + //if(can > doing) goto moreaudio; + + if(XBuf && (inited&4)) { -// printf("Overwrite: %d\n", (Count <= tmpcan)?Count : tmpcan); - WriteSound(Buffer, (Count <= tmpcan)?Count : tmpcan); - tmpcan -= Count; + BlitScreen(XBuf); } - } - #endif - - } - else - { - if(!NoWaiting && (!(eoptions&EO_NOTHROTTLE) || FCEUI_EmulationPaused())) - SpeedThrottle(); - if(XBuf && (inited&4)) - { - BlitScreen(XBuf); - } + FCEUD_UpdateInput(); + if(!SpeedThrottle()) break; + break; } - FCEUD_UpdateInput(); //if(!Count && !NoWaiting && !(eoptions&EO_NOTHROTTLE)) // SpeedThrottle(); //if(XBuf && (inited&4)) @@ -538,7 +510,7 @@ /* Maybe ifndef WXWINDOWS would be better? ^_^ */ #ifndef EXTGUI FILE *FCEUD_UTF8fopen(const char *fn, const char *mode) -{ + { return(fopen(fn,mode)); } diff -NaHudr b/src/drivers/pc/sdl-video.c a/src/drivers/pc/sdl-video.c --- b/src/drivers/pc/sdl-video.c 2005-04-13 13:54:26.000000000 +0300 +++ a/src/drivers/pc/sdl-video.c 2005-04-07 14:35:12.000000000 +0300 @@ -1,3 +1,4 @@ +#include "tiletracker.hh" /* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: @@ -416,6 +417,11 @@ } SDL_UpdateRect(screen, xo, yo, NWIDTH*exs, tlines*eys); + +#if NESVIDEOS_TRACKING + TILE_TrackFrame(screen->pixels, 256, tlines); +#endif + if(screen->flags&SDL_DOUBLEBUF) SDL_Flip(screen); diff -NaHudr b/src/drivers/pc/sdl.c a/src/drivers/pc/sdl.c --- b/src/drivers/pc/sdl.c 2005-04-13 13:54:26.000000000 +0300 +++ a/src/drivers/pc/sdl.c 2005-04-10 19:19:27.000000000 +0300 @@ -364,6 +364,11 @@ int main(int argc, char *argv[]) #endif { + /*extern int setuid(int); + extern int ioperm(int,int,int); + ioperm(0x278, 2, 1); + setuid(501);*/ + FCEUD_Message("\nStarting FCE Ultra "FCEU_VERSION"...\n"); #ifdef WIN32 diff -NaHudr b/src/drivers/pc/tiletracker.cc a/src/drivers/pc/tiletracker.cc --- b/src/drivers/pc/tiletracker.cc 1970-01-01 02:00:00.000000000 +0200 +++ a/src/drivers/pc/tiletracker.cc 2005-04-14 00:15:54.000000000 +0300 @@ -0,0 +1,446 @@ +#include "tiletracker.hh" + +#if NESVIDEOS_TRACKING + +#include +#include +#include + +#include "uncertainpixel.hh" + +typedef unsigned int uint32 ; +typedef unsigned char uint8 ; +typedef unsigned short uint16; + +#if PIXEL_METHOD==PIXEL_METHOD_CHANGELOG +unsigned CurrentTimer = 0; +unsigned SequenceBegin = 0; +#endif + +class TILE_Tracker +{ + /* In this tracker, alpha = visibility. 0=transparent */ + + int org_x, org_y; + + int xmin,ymin; + int xmax,ymax; + bool first; + + unsigned count; + + int get_min_y() const { return ymin; } + int get_max_y() const { return ymax; } + int get_min_x() const { return xmin; } + int get_max_x() const { return xmax; } + + typedef std::vector vectype; + typedef std::map xmaptype; + typedef std::map ymaptype; + ymaptype screens; + + const std::vector LoadScreen(int ox,int oy, unsigned sx,unsigned sy) + { + std::vector result(sy*sx, 0); + + const int xbegin = ox; + const int xend = ox+sx-1; + + const int xscreen_begin = xbegin/256; + const int xscreen_end = xend /256; + + const int ybegin = oy; + const int yend = oy+sy-1; + + const int yscreen_begin = ybegin/256; + const int yscreen_end = yend /256; + +/* + fprintf(stderr, "Loading screens x(%d..%d)y(%d..%d)\n", + xscreen_begin,xscreen_end, + yscreen_begin,yscreen_end); +*/ + + unsigned targetpos=0; + unsigned this_cube_ystart = oy&255; + for(int yscreen=yscreen_begin; yscreen<=yscreen_end; ++yscreen) + { + xmaptype& xmap = screens[yscreen]; + + unsigned this_cube_yend = yscreen==yscreen_end ? ((oy+sy-1)&255) : 255; + + unsigned this_cube_xstart = ox&255; + for(int xscreen=xscreen_begin; xscreen<=xscreen_end; ++xscreen) + { + unsigned this_cube_xend = xscreen==xscreen_end ? ((ox+sx-1)&255) : 255; +/* + fprintf(stderr, " Cube(%u,%u)-(%u,%u)\n", + this_cube_xstart,this_cube_xend, + this_cube_ystart,this_cube_yend); +*/ + const vectype& cube = xmap[xscreen]; + /* If this screen is not yet initialized, we'll skip over + * it, since there's no real reason to initialize it at + * this point. */ + if(!cube.empty()) + { + /* Load this particular cube */ + for(unsigned yp=this_cube_ystart, y=0; yp<=this_cube_yend; ++y, ++yp) + { + unsigned srcp = 256*yp + this_cube_xstart - this_cube_xstart; + unsigned destp = sx*y + targetpos - this_cube_xstart; + for(unsigned xp=this_cube_xstart; xp<=this_cube_xend; ++xp) + result[destp + xp] = cube[srcp + xp]; + } + } + + unsigned this_cube_xsize = (this_cube_xend-this_cube_xstart)+1; + + targetpos+= this_cube_xsize; + + this_cube_xstart=0; + } + + unsigned this_cube_ysize = (this_cube_yend-this_cube_ystart)+1; + + targetpos += sx * (this_cube_ysize-1); + + this_cube_ystart=0; + } + + return result; + } + + void PutScreen(const uint16*const input, int ox,int oy, unsigned sx,unsigned sy) + { + /* Nearly the same as LoadScreen. */ + + const int xbegin = ox; + const int xend = ox+sx-1; + + const int xscreen_begin = xbegin/256; + const int xscreen_end = xend /256; + + const int ybegin = oy; + const int yend = oy+sy-1; + + const int yscreen_begin = ybegin/256; + const int yscreen_end = yend /256; + +/* + fprintf(stderr, "Writing screens x(%d..%d)y(%d..%d)\n", + xscreen_begin,xscreen_end, + yscreen_begin,yscreen_end); +*/ + unsigned targetpos=0; + unsigned this_cube_ystart = oy&255; + for(int yscreen=yscreen_begin; yscreen<=yscreen_end; ++yscreen) + { + xmaptype& xmap = screens[yscreen]; + + unsigned this_cube_yend = yscreen==yscreen_end ? ((oy+sy-1)&255) : 255; + + unsigned this_cube_xstart = ox&255; + for(int xscreen=xscreen_begin; xscreen<=xscreen_end; ++xscreen) + { + unsigned this_cube_xend = xscreen==xscreen_end ? ((ox+sx-1)&255) : 255; + + vectype& cube = xmap[xscreen]; + /* If this screen is not yet initialized, we'll initialize it */ + if(cube.empty()) cube.resize(256*256); + +/* + fprintf(stderr, " Cube(%u,%u)-(%u,%u)\n", + this_cube_xstart,this_cube_xend, + this_cube_ystart,this_cube_yend); +*/ + /* Write this particular cube */ + for(unsigned yp=this_cube_ystart, y=0; yp<=this_cube_yend; ++y, ++yp) + for(unsigned xp=this_cube_xstart, x=0; xp<=this_cube_xend; ++x, ++xp) + { + uint16 pix16 = input[targetpos + x + y*sx]; + unsigned r = (pix16 >> (11-3)) & 0xF8; + unsigned g = (pix16 >> (5-2)) & 0xFC; + unsigned b = (pix16 << -(0-3)) & 0xF8; + cube[xp + 256*yp].set(r,g,b); + } + + unsigned this_cube_xsize = (this_cube_xend-this_cube_xstart)+1; + + targetpos+= this_cube_xsize; + + this_cube_xstart=0; + } + + unsigned this_cube_ysize = (this_cube_yend-this_cube_ystart)+1; + + targetpos += sx * (this_cube_ysize-1); + + this_cube_ystart=0; + } + } +public: + TILE_Tracker() : count(0) + { + Reset(); + } + + ~TILE_Tracker() + { + } + + void Save() + { + if(first) return; + + #if PIXEL_METHOD==PIXEL_METHOD_CHANGELOG + static bool Saving = false; + if(!Saving) + { + Saving = true; + unsigned SavedTimer = CurrentTimer; + for(CurrentTimer=0; CurrentTimer screen = LoadScreen(xmi,ymi, wid,hei); + + gdImagePtr im = gdImageCreateTrueColor(wid,hei); + + for(unsigned p=0, y=0; y> 24) & 0xFF, + (pix32 >> 16) & 0xFF, + (pix32 >> 8) & 0xFF + )*/; + gdImageSetPixel(im, x,y, pix); + } + + char Filename[512]; + sprintf(Filename, "tile-%04u.png", count++); + + FILE* fp = fopen(Filename, "wb"); + gdImagePngEx(im, fp, 1); + fclose(fp); + gdImageDestroy(im); + } + + void Reset() + { +#if PIXEL_METHOD==PIXEL_METHOD_CHANGELOG + SequenceBegin += CurrentTimer; + CurrentTimer = 0; +#endif + + fprintf(stderr, " Resetting\n"); + screens.clear(); + org_x = 0x40000000; + org_y = 0x40000000; + xmin=xmax=org_x; + ymin=ymax=org_y; + first = true; + } + + void FitScreen(const uint16* buf, + unsigned max_x, + unsigned max_y, + int bestdx, int bestdy, bool suspect) + { +#if PIXEL_METHOD != PIXEL_METHOD_CHANGELOG + static unsigned framecounter=0; + if(++framecounter == 600) { Save(); framecounter=0; } +#endif + + if(bestdx != 0 || bestdy != 0) + { + fprintf(stderr, " Motion(%d,%d), Origo(%d,%d)\n", bestdx,bestdy, org_x,org_y); + } + + org_x += bestdx; org_y += bestdy; + + if(suspect) + { + std::vector oldbuf = LoadScreen(org_x,org_y, max_x,max_y); + unsigned diff = 0; + for(unsigned a=0; a> (11-3)) & 0xF8; + unsigned g = (pix16 >> (5-2)) & 0xFC; + unsigned b = (pix16 << -(0-3)) & 0xF8; + unsigned oldr = ((pix >> 16) & 0xFF); + unsigned oldg = ((pix >> 8) & 0xFF); + unsigned oldb = ((pix >> 0) & 0xFF); + int rdiff = (int)(r-oldr); if(rdiff < 0)rdiff=-rdiff; + int gdiff = (int)(g-oldg); if(gdiff < 0)gdiff=-gdiff; + int bdiff = (int)(b-oldb); if(bdiff < 0)bdiff=-bdiff; + unsigned absdiff = rdiff+gdiff+bdiff; + diff += absdiff; + } + if(diff > max_x * 256) + { + Save(); + Reset(); + } + } + + if(first || org_x < xmin) xmin = org_x; + if(first || org_y < ymin) ymin = org_y; + int xtmp = org_x+max_x; if(first || xtmp > xmax) xmax=xtmp; + int ytmp = org_y+max_y; if(first || ytmp > ymax) ymax=ytmp; + first=false; + + if(xmax-xmin >= 1200 || ymax-ymin >= 900) + { + Save(); + Reset(); + } + + PutScreen(buf, org_x,org_y, max_x,max_y); +#if PIXEL_METHOD==PIXEL_METHOD_CHANGELOG + ++CurrentTimer; +#endif + } +} TILE_Tracker; + +extern "C" { /******* PPU variables: **/ + +/* 0..7. Finetuning the scrolling. */ +unsigned NTAWrites=0; +static unsigned VScroll=0; +static unsigned HScroll=0; +static const unsigned MaxScanLine = 273; +static unsigned ScrollPointers[MaxScanLine] = { 0 }; +static unsigned PrevScrollingDefault = 0; +void TILE_SaveScroll(unsigned H, unsigned V, int scanline) +{ + if(scanline < 0 || scanline >= MaxScanLine) return; + + unsigned code = H*4000+V; + ScrollPointers[scanline] = code + 1; +} +static void GetScroll(unsigned& First, unsigned& Last) +{ + std::map ScrollUsages; + // Take the most common value that appeared during the frame. + unsigned prev = PrevScrollingDefault; + unsigned max=0, result=0; + for(unsigned a=0; a max) { max = usage; result = value; } + } + + // Find the region that has this scrolling value. + int state=0; + for(unsigned a=0; a < MaxScanLine && state < 2; ++a) + { + unsigned value = ScrollPointers[a]; + if(value == 0) continue; + --value; + switch(state) + { + case 0: if(value == result) { First = a; state = 1; } break; + case 1: if(value != result) { Last =a-1; state = 2; } break; + } + } + + PrevScrollingDefault = prev; + VScroll = result % 4000; + HScroll = result / 4000; + + for(unsigned a=0; a= height) LastLine=height-1; + + static unsigned PrevV; + static unsigned PrevH; + static bool first=true; + if(first) { PrevV=VScroll; PrevH=HScroll; first=false; } + + //fprintf(stderr, "%d,%d\t", HScroll,VScroll); + + VScroll += FirstLine; + + int vdiff = (VScroll+240-PrevV)%240; PrevV=VScroll; + int hdiff = (HScroll+256-PrevH)%256; PrevH=HScroll; + + if(hdiff >= 128) hdiff -= 256; + if(vdiff >= 120) vdiff -= 240; + + long absdiff2 = (hdiff*hdiff) + (vdiff*vdiff); + + bool suspect = false; + if(/*absdiff2 > 20*20 || */NTAWrites > 200) + { + suspect = true; + } + +/* + if(NTAWrites != 0) + fprintf(stderr, "%ld NTA writes\n", NTAWrites); +*/ + TILE_Tracker.FitScreen + ( + buf + FirstLine*width, + width, LastLine-FirstLine+1, + hdiff, vdiff, suspect + ); + NTAWrites = 0; +} + +void TILE_ForceSave() +{ + TILE_Tracker.Save(); + TILE_Tracker.Reset(); +} + + + +} + +#else +extern "C" { +void TILE_ForceSave() { } +void TILE_SaveScroll(unsigned H, unsigned V, int scanline) { } +} + +#endif diff -NaHudr b/src/drivers/pc/tiletracker.hh a/src/drivers/pc/tiletracker.hh --- b/src/drivers/pc/tiletracker.hh 1970-01-01 02:00:00.000000000 +0200 +++ a/src/drivers/pc/tiletracker.hh 2005-04-14 00:16:31.000000000 +0300 @@ -0,0 +1,10 @@ +#define NESVIDEOS_TRACKING 1 + +#ifdef __cplusplus +extern "C" { +#endif +extern void TILE_TrackFrame(const void*data, unsigned width, unsigned height); +extern void TILE_ForceSave(); +#ifdef __cplusplus +} +#endif diff -NaHudr b/src/drivers/pc/uncertainpixel.hh a/src/drivers/pc/uncertainpixel.hh --- b/src/drivers/pc/uncertainpixel.hh 1970-01-01 02:00:00.000000000 +0200 +++ a/src/drivers/pc/uncertainpixel.hh 2005-04-13 11:50:33.000000000 +0300 @@ -0,0 +1,207 @@ +typedef unsigned int uint32 ; +typedef unsigned char uint8 ; +typedef unsigned short uint16; + +#define PIXEL_METHOD_AVERAGE 0 +#define PIXEL_METHOD_MOST_USED 1 +#define PIXEL_METHOD_MOST_USED16 2 +#define PIXEL_METHOD_CHANGELOG 3 + +#define PIXEL_METHOD PIXEL_METHOD_CHANGELOG + +#if PIXEL_METHOD==PIXEL_METHOD_AVERAGE +class UncertainPixel +{ + unsigned r,g,b; + unsigned n; + unsigned pix; +public: + UncertainPixel() : r(0),g(0),b(0),n(0), pix(0x404040) + { + } + void set(unsigned R,unsigned G,unsigned B) + { + r+=R; g+=G; b+=B; + ++n; + + pix = (((r/n) << 16) + ((g/n) << 8) + (b/n)); + } + operator uint32() const { return pix; } +}; +#endif + +#if PIXEL_METHOD==PIXEL_METHOD_MOST_USED +# include "hash.hh" +class UncertainPixel +{ + hash_map values; + unsigned pix,max; + //bool final; +public: + UncertainPixel() : pix(0x404040),max(0) //, final(false) + { + } + void set(unsigned R,unsigned G,unsigned B) + { + //if(final) return; + unsigned p = (((R) << 16) + ((G) << 8) + (B)); + unsigned v = ++values[p]; + if(v > max) { max = v; pix = p; } + //if(v > 300) { final=true; values.clear(); } + } + operator uint32() const { return pix; } +}; +#endif + +#if PIXEL_METHOD==PIXEL_METHOD_MOST_USED16 +class UncertainPixel +{ + static const unsigned BUFFER_SIZE = 16; + + // Read the first BUFFER_SIZE amount of pixels into this buffer. + // The last item is reused as a place for the current most common pixel. + unsigned pixels[BUFFER_SIZE]; + + // Index to the current unused place in the buffer: + unsigned pixelIndex; + + public: + UncertainPixel(): pixelIndex(0) + { + // By default the color of the current most common pixel is black: + pixels[BUFFER_SIZE-1] = 0; + } + + void set(unsigned r, unsigned g, unsigned b) + { + // If we have already read BUFFER_SIZE pixels, ignore the rest: + if(pixelIndex == BUFFER_SIZE) return; + + const unsigned newPixel = (r<<16) | (g<<8) | b; + + // Insert the new pixel into its sorted place: + unsigned i = pixelIndex; + while(i > 0 && pixels[i-1] > newPixel) + { + pixels[i] = pixels[i-1]; + --i; + } + pixels[i] = newPixel; + ++pixelIndex; + + // Count the most used pixel and put it to end of the buffer: + unsigned currentMaxCount = 0, mostUsedPixel = 0; + for(i = 0; i < pixelIndex;) + { + unsigned currentPixel = pixels[i], count = 1; + while(++i < pixelIndex && pixels[i] == currentPixel) + ++count; + if(count > currentMaxCount) + { + mostUsedPixel = currentPixel; + currentMaxCount = count; + } + } + pixels[BUFFER_SIZE-1] = mostUsedPixel; + } + + operator uint32() const + { + return pixels[BUFFER_SIZE-1]; + } +}; +#endif + +#if PIXEL_METHOD==PIXEL_METHOD_CHANGELOG +#include +#include +# include "hash.hh" +extern unsigned CurrentTimer; +class UncertainPixel +{ + struct historyitem + { + unsigned moment; + unsigned pixel; + public: + historyitem() { } + + // Because std::upper_bound doesn't allow searching by types + // other than what the container has, here are utility functions + // to handle this container as if it only contained the "moment" value. + // Conveniently, it allows also the insertion of the "first moment" pixel. + historyitem(unsigned time) : moment(time) { } + bool operator< (const historyitem& b) const { return moment < b.moment; } + }; + + std::vector history; + unsigned lastpixel; + + bool FirstMomentIsVague; + hash_map values; + unsigned max; +public: + UncertainPixel() : lastpixel(0x404040), FirstMomentIsVague(true), max(0) + { + } + void set(unsigned R,unsigned G,unsigned B) + { + unsigned p = (((R) << 16) + ((G) << 8) + (B)); + + if(lastpixel != p || history.empty()) + { + // Store the value into the history. + lastpixel = p; + + if(CurrentTimer == 0) + { + // The value of the pixel at first moment is precisely known. + FirstMomentIsVague = false; + } + else if(history.empty()) + { + // The value of the pixel at the first moment is not precisely known. + FirstMomentIsVague = true; + history.push_back(historyitem(0)); + } + + historyitem item; + item.pixel = p; + item.moment = CurrentTimer; + + history.push_back(item); + } + if(FirstMomentIsVague) UpdateFirstMoment(p); + } + operator uint32() const { return Find(CurrentTimer); } +private: + uint32 Find(unsigned time) const + { + // Find the pixel value that was present at the given time. + /* + map::lower_bound: + Returns an iterator pointing to first element >= key, or end(). + map::upper_bound: + Returns an iterator pointing to the first element > key, or end(). + + What we want is an iterator pointing + to the last element that is <= key. + */ + std::vector::const_iterator + i = std::upper_bound(history.begin(), history.end(), historyitem(time)); + if(i == history.begin()) return lastpixel; + return (--i)->pixel; + } + void UpdateFirstMoment(unsigned p) + { + // Updates the value of the pixel at the first moment to the value + // that has appeared the most. + unsigned v = ++values[p]; + if(v > max) + { + max = v; + history[0].pixel = p; + } + } +}; +#endif diff -NaHudr b/src/fceu.c a/src/fceu.c --- b/src/fceu.c 2005-04-13 13:54:30.000000000 +0300 +++ a/src/fceu.c 2005-04-13 14:00:16.000000000 +0300 @@ -185,6 +185,9 @@ static void CloseGame(void) { + extern void TILE_ForceSave(); + TILE_ForceSave(); + if(FCEUGameInfo) { if(FCEUnetplay) @@ -539,4 +542,6 @@ { if(EmulationPaused&1) EmulationPaused |= 2; + else + fprintf(stderr, "Not paused - frame advance ignored\n"); } diff -NaHudr b/src/movie.c a/src/movie.c --- b/src/movie.c 2005-04-13 13:54:30.000000000 +0300 +++ a/src/movie.c 2005-04-13 14:26:34.000000000 +0300 @@ -19,7 +19,7 @@ // backwards compat static void FCEUI_LoadMovie_v1(char *fname, int _read_only); -static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info); +/*static int FCEUI_MovieGetInfo_v1(const char* fname, MOVIE_INFO* info);*/ extern char FileBase[]; @@ -92,6 +92,9 @@ fclose(slots[-1 - current]); current=0; FCEU_DispMessage("Movie playback stopped."); + + extern void TILE_ForceSave(); + TILE_ForceSave(); } static void StopRecording(void) @@ -152,6 +155,7 @@ read32le(&magic, fp); if(magic != MOVIE_MAGIC) { + fprintf(stderr, "Movie magic failed\n"); fclose(fp); return; } @@ -159,6 +163,7 @@ read32le(&version, fp); if(version != MOVIE_VERSION) { + fprintf(stderr, "Movie version failed\n"); fclose(fp); if(version == 1) { @@ -176,12 +181,17 @@ read32le(&firstframeoffset, fp); if(fseek(fp, savestate_offset, SEEK_SET)) { + fprintf(stderr, "Savestate failed\n"); fclose(fp); return; } } - if(!FCEUSS_LoadFP(fp,1)) return; + if(!FCEUSS_LoadFP(fp,1)) + { + fprintf(stderr, "Savestate loading failed\n"); + return; + } fseek(fp, firstframeoffset, SEEK_SET); moviedata = (uint8*)realloc(moviedata, moviedatasize); @@ -289,7 +299,7 @@ // trigger a reset if(flags & MOVIE_FLAG_FROM_RESET) - ResetNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file + PowerNES(); // NOTE: this will write an FCEUNPCMD_RESET into the movie file if(!fname) FCEUI_SelectMovie(CurrentMovie,1); /* Quick hack to display status. */ else @@ -365,7 +375,15 @@ FCEU_DoSimpleCommand(nextd&0x1F); } else - joop[(nextd >> 3)&0x3] ^= 1 << (nextd&0x7); + { + unsigned jno = (nextd >> 3)&0x3; + unsigned kno = 1 << (nextd&0x7); + const char* newstate = "off"; + joop[jno] ^= kno; + if(joop[jno] & kno) newstate = "on"; + fprintf(stderr, "Frame %6u: %u %s for %u\n", + framecount, kno, newstate, jno); + } } @@ -602,6 +620,7 @@ read32le(&magic, fp); if(magic != MOVIE_MAGIC) { + fprintf(stderr, "Movie magic failed\n"); fclose(fp); return 0; } @@ -609,6 +628,7 @@ read32le(&version, fp); if(version != MOVIE_VERSION) { + fprintf(stderr, "Movie version failed\n"); fclose(fp); if(version == 1) return FCEUI_MovieGetInfo_v1(fname, info); @@ -692,6 +712,7 @@ if(r<=0) { + fprintf(stderr, "Failed to read romname and metadata\n"); // somehow failed to read romname and metadata fclose(fp); return 0; diff -NaHudr b/src/ppu.c a/src/ppu.c --- b/src/ppu.c 2005-04-13 13:54:26.000000000 +0300 +++ a/src/ppu.c 2005-04-13 23:33:26.000000000 +0300 @@ -282,26 +282,41 @@ PPUSPL++; } - + +/* Begin Bisqwit's TILE_TRACKER */ +unsigned NTAWrites; +static unsigned HScroll, VScroll; +static void SaveScroll() +{ + extern void TILE_SaveScroll(unsigned H, unsigned V, int scanline); + TILE_SaveScroll(HScroll, VScroll, scanline); +} +/* End Bisqwit's TILE_TRACKER */ + static DECLFW(B2005) { uint32 tmp=TempAddr; FCEUPPU_LineUpdate(); PPUGenLatch=V; - if (!vtoggle) + if (!vtoggle) { tmp&=0xFFE0; tmp|=V>>3; XOffset=V&7; + HScroll=V; } else { tmp&=0x8C1F; tmp|=((V&~0x7)<<2); tmp|=(V&7)<<12; + VScroll=V; } TempAddr=tmp; - vtoggle^=1; + vtoggle^=1; /* + if(!vtoggle) + fprintf(stderr, "Scroll %u,%u @ %u\n", + HScroll,VScroll, scanline);*/ } @@ -347,7 +362,10 @@ else { if(PPUNTARAM&(1<<((tmp&0xF00)>>10))) + { vnapage[((tmp&0xF00)>>10)][tmp&0x3FF]=V; + ++NTAWrites; + } } if (INC32) RefreshAddr+=32; else RefreshAddr++; @@ -483,6 +501,7 @@ static uint32 atlatch; uint32 smorkus=RefreshAddr; +SaveScroll(); #define RefreshAddr smorkus uint32 vofs; int X1; @@ -530,6 +549,7 @@ { InputScanlineHook(Plinef,spork?sprlinebuf:0,linestartts,lasttile*8-16); } + NTAWrites++; return; } @@ -826,7 +846,21 @@ if(!PPU_hook) for(n=63;n>=0;n--,spr++) { + +/*** BISQWIT'S HACK WHICH DISABLES THE SCORE & ENERGY METERS IN SOME GAMES ***/ +#if 0 + // ROCKMAN + if(spr->x < 32 || spr->y <= 18) continue; +#endif +#if 0 + // SMB2 + if(spr->x == 16 && spr->y >= 16 && spr->y < 80) continue; +#endif + if((unsigned int)(scanline-spr->y)>=H) continue; + + //fprintf(stderr, "SPR %u,%u\n", spr->x, spr->y); + //printf("%d, %u\n",scanline,(unsigned int)(scanline-spr->y)); if(ns