/* ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net This code is covered by the GNU GPL licence */ #include "CEMulatorCtrl.h" #include "svnversion.h" #include "../kegs/Src/adb.h" #include "../kegs/Src/clock.h" #include "../kegs/Src/moremem.h" #include "../kegs/Src/paddles.h" #include "../kegs/Src/sim65816.h" #include "../kegs/Src/iwm.h" #include "../kegs/Src/SaveState.h" #include "../kegs/Src/raster.h" #include "../kegs/Src/graphcounter.h" #include "../kegs/Src/driver.h" #include "../kegs/Src/video.h" #include "../kegs/Src/async_event.h" extern void joystick_shut(); #define CONFIG_VERSION 3 const int joyint[] = { (int)JOYSTICK_TYPE_NATIVE_1, (int)JOYSTICK_TYPE_KEYPAD, (int)JOYSTICK_TYPE_MOUSE, /* JOYSTICK_TYPE_NATIVE_2,*/ JOYSTICK_TYPE_NONE, JOYSTICK_TYPE_ICADE, -1 } ; const char* joyalias[] = { "joystick1","keypad", "mouse", /*, "Joystick2",*/ "none", "iCade", NULL }; const char* joydesc[] = { "Joystick1","Keypad", "Mouse", /*, "Joystick2",*/ "None", "iCade", NULL }; const int colorint[] = { (int)COLORMODE_AUTO, (int)COLORMODE_BW, (int)COLORMODE_GREEN, (int)COLORMODE_AMBER,(int)COLORMODE_MONO, -1 }; const char* coloralias[] = { "auto", "bw", "green", "amber", "mono",NULL }; const char* colordesc[] = { "Colors", "B&W", "Green", "Amber", "Mono", NULL }; const char* videoalias[] = { "crt" , "lcd" ,"default", NULL }; const char* videodesc[] = { "CRT" , "LCD",NULL, NULL }; const int videoint[] = { (int)VIDEOFX_CRT, (int)VIDEOFX_LCD, (int)VIDEOFX_LCD, -1 }; const char* speeddesc[] = { "Normal", "Slow", "Zip", "Unlimited", NULL }; const char* speedalias[] = { "normal", "slow", "zip", "unlimited", NULL }; const int speedint[] = { (int)SPEED_GS, (int)SPEED_1MHZ, (int)SPEED_ZIP, (int)SPEED_UNLIMITED, -1 }; #define DEFAULT_AUDIO_RATE 44100 #define DEFAULT_LOCKZOOM 0 #define DEFAULT_DEBUGMODE 0 #define DEFAULT_DISKSOUND 1 #define DEFAULT_VIDEO VIDEOFX_CRT const namevaluedef defoptions[] = { { 1,0, "configVersion" , CONFIG_VERSION,NULL, NULL, NULL }, { 1,0, "guid" , 0, NULL, NULL, NULL }, { 1,1, "haltOnBadRead" , 0, NULL, NULL, NULL }, { 1,1, "haltOnBadAcc" , 0, NULL, NULL, NULL }, { 1,1, "haltOnHalts" , 0, NULL, NULL, NULL }, { 1,1, "enableRewind" ,0, NULL, NULL, NULL }, { 1,1, "colorMode" , 0, NULL, colordesc,coloralias , colorint }, { 1,1, "videoFX", (int)DEFAULT_VIDEO, NULL, videodesc, videoalias , videoint }, { 1,1, "joystickMode", 0, NULL, joydesc, joyalias, joyint }, { 0,0, "browserName" , 0, NULL, NULL, NULL }, { 0,0, "browserVer" , 0, NULL, NULL, NULL }, { 2,0, "latestRunBuild" , 0, NULL, NULL, NULL }, { 2,0, "latestUpdateCheck" , 0, NULL, NULL, NULL }, { 1,0, "doNotShowUpdateAlert" , 0, NULL, NULL, NULL }, { 0,0, "doNotShowDiskStatus" , 0, NULL, NULL, NULL }, { 0,0, "PNGBorder" , 16, NULL, NULL, NULL }, { 0,0, "simulateSpace" , 0X31, NULL, NULL, NULL }, // 0X31=SPACE KEY { 0,0, "latestVersion" , 0, NULL, NULL, NULL }, { 0,0, "speed" , 0, NULL, speeddesc, speedalias, speedint }, { 0,0, "latestVersionDate" , 0, NULL, NULL, NULL }, { 1,1, "audioRate" , DEFAULT_AUDIO_RATE, NULL, NULL, NULL }, { 1,1, "memorySize" , 2, NULL, NULL, NULL }, { 0,0, "joyXScale" , 1000, NULL, NULL, NULL }, { 0,0, "joyYScale" , 1000, NULL, NULL, NULL }, { 1,1, "diskSound" , DEFAULT_DISKSOUND, NULL, NULL, NULL }, { 0,1, "fixedVBlank" , 0, NULL, NULL, NULL }, { 0,1, "adbDelay" , 45, NULL, NULL, NULL }, { 0,1, "adbRate" , 3, NULL, NULL, NULL } #ifdef DRIVER_IOS , { 1,1, "frameRate" , 0, NULL, NULL, NULL }, { 1,1, "lockZoom" , DEFAULT_LOCKZOOM, NULL, NULL, NULL }, { 1,1, "lastTab" , 0, NULL, NULL, NULL }, { 1,1, "debugMode" , DEFAULT_DEBUGMODE, NULL, NULL, NULL }, { 1,1, "externalKBD" , 0, NULL, NULL, NULL } #endif #ifdef ACTIVEGSKARATEKA , { 1,1, "showIcons" , 1, NULL, NULL, NULL }, { 0,1, "enableCheats" , 0, NULL, NULL, NULL }, { 1,1, "soundMode" , 0, NULL, NULL, NULL }, { 1,1, "karatekaRewind" , 1, NULL, NULL, NULL }, #endif #ifdef VIRTUALAPPLE { 1,1, "debug" , 0, NULL, NULL, NULL }, { 1,1, "scores" , 0, NULL, NULL, NULL }, { 1,1, "gamecenter" , 0, NULL, NULL, NULL }, #endif }; COption option; const char* COption::getDescription(option_id _id, int _value) { namevalue& nv = find(_id);; return nv.getDescription(_value); } int namevalue::belongsToValue(int _v) { if (!def->convertTableInt) return 1; int i=0; while(def->convertTableInt[i]!=-1) { if (def->convertTableInt[i]==_v) return 1; i++; } return 0; } int namevalue::getOrder(int _value) { int p = 0; while(def->convertTableInt[p] != -1) { if (def->convertTableInt[p]==_value) return p; p++; } return -1; } const char* namevalue::getDescription(int _value) { const char* ret = "n/a"; if (!def->convertTableInt) return ret; int o = getOrder(_value); if (o<0) return ret; return def->convertTableDesc[o]; } CEmulator* CEmulator::theEmulator = NULL; char bram_default[]={ (char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0x00, (char)0x00, (char)0x0D, (char)0x06, (char)0x02, (char)0x01, (char)0x01, (char)0x00, (char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x07, (char)0x06, (char)0x02, (char)0x01, (char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x0F, (char)0x06, (char)0x06, (char)0x00, (char)0x05, (char)0x06, (char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x03, (char)0x02, (char)0x02, (char)0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x01, (char)0x02, (char)0x03, (char)0x04, (char)0x05, (char)0x06, (char)0x07, (char)0x00, (char)0x00, (char)0x01, (char)0x02, (char)0x03, (char)0x04, (char)0x05, (char)0x06, (char)0x07, (char)0x08, (char)0x09, (char)0x0A, (char)0x0B, (char)0x0C, (char)0x0D, (char)0x0E, (char)0x0F, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFD, (char)0x96, (char)0x57, (char)0x3C }; void updateBramCheckSum(unsigned char* buf) // original code in FF/B61D { register short int crc=0; // LDA #0 int i; // CLC for(i=255-4-1;i>=0;i--) // LDX #FA { crc = (crc << 1) | ((crc>>15) & 1); // ROL (1-bits) #ifdef UNDER_CE crc += buf[i]; crc += buf[i+1]*256; #else crc += *(unsigned short*)(buf+i); // ADC bram,X #endif } // DEX // CPX #FF // BNE Loop *(unsigned short*)(buf+252) = crc; // TAX *(unsigned short*)(buf+254) = crc ^0xAAAA; // EOR #AAAA } void initializeBram(unsigned char* _bram_ptr) { memcpy(_bram_ptr,bram_default,256); updateBramParameters(_bram_ptr); updateBramCheckSum(_bram_ptr); } void COption::generateGUID(MyString& guid) { int crc = calcCRC(CDownload::getPersistentPath()); time_t now; time( &now ); int lowtime = (int)now; guid.Format("%08X%08X",crc,lowtime); } void CEmulatorConfig::resetConfig() { speed = SPEED_AUTO; bootslot = -1; //auto // fastConfig = false; // pXML=NULL; // vire tous les slots for(int i=0;i<3;i++) { for(int m=0;mconvertTableInt) return 0; int i=0; const char* str; while( (str=def->convertTableAlias[i]) != NULL ) { if(!strcasecmp(_value,str)) return def->convertTableInt[i]; i++; } printf("err: could not convert %s : returning %s (%d)\n",_value,def->convertTableAlias[0],def->convertTableInt[0]); return def->convertTableInt[0]; } option_id COption::findByName(const char* _name) { if (!_name) return (option_id)-1; for (int i=0;iname)) return (option_id)i; } #ifdef _DEBUG printf("cannot find option : %s\n",_name); #endif return (option_id)-1; } void COption::setValue(option_id _id,const char* _value,int _confOverrided) { namevalue& nv = find(_id); int isnum=1; const char*p=_value; while(*p) { if (*p<'0' || *p>'9') { isnum=0; break; } p++; } int v; if (isnum) v=atoi(_value); if (!nv.def->convertTableInt) { if (!isnum) { nv.strvalue = _value; printf("> option %s: %s\n",nv.def->name,_value); } else { nv.intvalue = v; printf("> option %s: (%d)\n",nv.def->name,nv.intvalue); } nv.confOverrided = _confOverrided; } else { if (isnum && nv.belongsToValue(v)) { nv.intvalue = v; nv.confOverrided = _confOverrided; printf("> option %s: %s (%d)\n",nv.def->name,nv.getDescription(v),nv.intvalue); } else { // regarde si c'est une valeur en texte? int pos=0; const char* s; while( (s = nv.def->convertTableAlias[pos]) != NULL ) { if (!strcasecmp(s,_value)) { nv.intvalue = nv.def->convertTableInt[pos]; nv.confOverrided = _confOverrided; printf("* option %s: %s (%d)\n",nv.def->name,_value,nv.intvalue); return ; } pos++; } printf("non supported value:%s for option:%s\n",_value,nv.def->name); } } } void namevalue::getAlias(MyString& _str) { if (this->strvalue.IsEmpty()) { // convert to int first int v = this->intvalue; if (!this->def->convertTableInt) _str.Format("%d",v); else { int i = getOrder(v); _str = this->def->convertTableAlias[i]; } return ; } else _str = this->strvalue; } void COption::initOptions() { for(int i=0;iinDefault) { if (options[i].def->convertTableInt) options[i].intvalue =options[i].def->convertTableInt[0]; else { options[i].intvalue = options[i].def->defaultint; options[i].strvalue = options[i].def->defaultstr; } } } if (g_driver.x_apply_default_options!=NULL) g_driver.x_apply_default_options((void*)this); printf("*restored default options\n"); } void COption::setIntValue(option_id _id, int _v, int _confOverrided) { MyString str; namevalue& nv =find(_id); if (!nv.belongsToValue(_v)) { printf("err: unrecognized value %d for %s\n",_v,nv.def->name); return ; } nv.intvalue = _v; if (nv.def->convertTableInt) printf("option %s set to %s (%d)\n",nv.def->name,nv.getDescription(_v),_v); else printf("option %s set to (%d)\n",nv.def->name,_v); nv.confOverrided = _confOverrided; } void COption::getStrValue(MyString& _str,option_id _id) { namevalue& nv =find(_id); if (nv.strvalue.IsEmpty()) { if (nv.def->convertTableInt) _str = nv.getDescription(nv.intvalue); else _str.Format("%d",nv.intvalue); } else _str = nv.strvalue.c_str(); } int COption::getIntValue(option_id _id) { namevalue& nv =find(_id); if (!nv.def->convertTableInt || nv.strvalue.IsEmpty()) return nv.intvalue; else { printf("getintvalue: mismatched type for %s\n",nv.def->name); return 0; } } void COption::saveOptions(int _nb) { MyString filename; getParameterFilename(filename,_nb); FILE* f = fopen(filename.c_str(),"wt"); if (!f) { outputInfo("cannot create %s for saving options\n",filename.c_str()); return ; } for(int i=0;iinConfig == _nb) { MyString line; MyString desc; options[i].getAlias(desc); line.Format("%s:%s\n",options[i].def->name,desc.c_str()); fputs(line.c_str(),f); } } fclose(f); //outputInfo("options saved (%s)\n",filename.c_str()); enableOptions(); } void COption::loadOptions() { int b = loadParameters(1); if (!b) { // si il n'y avait pas de fichier conf - rajoute le guid au option par défaut et sauve le MyString guid; generateGUID(guid); setValue(OPTION_GUID,guid.c_str()); saveOptions(1); } // vérifie le numéro de version int v = getIntValue(OPTION_CONFIGVERSION); if (v < CONFIG_VERSION) { // réinitialise initOptions(); setIntValue(OPTION_CONFIGVERSION,CONFIG_VERSION); printf("upgrading config file to v" TOSTRING(CONFIG_VERSION)); saveOptions(1); } loadParameters(2); } void COption::getParameterFilename(MyString& filename,int _nb) { filename = CDownload::getPersistentPath(); filename += ACTIVEGS_DIRECTORY_SEPARATOR ; filename += "activegs"; if (_nb==1) filename += ".conf"; else filename += ".ext"; } int COption::loadParameters(int _nb) { MyString filename; getParameterFilename(filename,_nb); FILE* f = fopen(filename,"rt"); if (f) { outputInfo("loadOptions from %s\n",filename.c_str()); char line[1025]; line[1024]=0; while(fgets(line,1024,f)) { int i=0; while(line[i]!=':' && line[i])i++; if (line[i]==':') { line[i++]=0; option_id id = findByName(line); if (id == OPTION_ERROR) outputInfo("option %s ignored\n",line); else { int vpos = i; while( line[i] && (line[i]!='\t') && (line[i]!='\r') && (line[i]!='\n') /* && (line[i]!=' ') */) i++; if (line[vpos]) { // trim while(line[i-1]==' ')i--; line[i]=0; setValue(id,line+vpos); } } } } fclose(f); return 1; } else return 0; } CEmulatorConfig::CEmulatorConfig() { xmlAlreadyLoaded = 0; pXML=NULL; bramParam.Empty(); emulatorParam.Empty(); nb_bps=0; resetConfig(); } void CEmulatorConfig::setBaseURL(const char* _url) { MyString p = _url; if (!strncasecmp(p.c_str(),"file://localhost/",17)) baseURL = p.substr(16,p.length()-16); // absolute path OG 250810 - leave the trailing slash else if (!strncasecmp(p.c_str(),"file:///",8)) #ifdef WIN32 baseURL = p.substr(8,p.length()-8); // absolute path #else baseURL = p.substr(7,p.length()-7); // keep the / on mac #endif else if (!strncasecmp(p.c_str(),"file://",7)) baseURL = p.substr(7,p.length()-7); // absolute path else baseURL = p; baseURL.Replace("%20"," "); printf("baseURL set to %s\n",baseURL.c_str()); /* CDownload dl; dl.setBaseURL(baseURL.c_str()); */ } CSlotInfo& CEmulatorConfig::getLocalImage(int _slot, int _drive, int _pos) { return localImages[_slot-5][_drive-1][_pos]; } CActiveImage& CEmulatorConfig::getActiveImage(int _slot, int _drive) { return activeImages[_slot-5][_drive-1]; } CSlotInfo* CEmulatorConfig::setSlot(int _slot, int _disk, const char * _url_normalized, enum eSetSlotPos _pos) { if (_slot<5 || _slot > 7 || _disk < 1 || (_slot==7 && _disk > MAXSLOT7DRIVES) || (_slot!=7 && _disk > 2) ) { printf("invalid slot : %d:%d\n",_slot,_disk); return NULL; } // normalise pour garder la compatibilité avec les version < 3.5 MyString url = _url_normalized; int pos = url.ReverseFind('?'); if (pos != -1 ) { int i = url.at(pos+1); size_t l = url.length(); if (l==(pos+2) && i>='0' && i<='9') { url = url.Left(pos); url += "#pos="; url += i; } } const char* _url = url.c_str(); if (_url && !_url[0]) _url=0; CActiveImage& ai = activeImages[_slot-5][_disk-1 ]; int active=0; if (_pos==REPLACECURRENT) // posactive { active = ai.iActive; if (!ai.iNb) { // ignore SetSlot = NULL si aucun disk n'avait été préalablement chargé if (!_url) return NULL; ai.iNb=1; } } else // if (_pos==ADD) { // regarde si le disk était déjà présent dans la liste for(int i =0;i2048) { // overflow! outputInfo("status set to FAILED(invalid length) for S%dD%d\n",_slot,_disk); ref.status = FAILED; } else { ref.url = _url; ref.url.Trim(); // OG Fix ending space ref.shortname = getfile(_url); ref.status = READY2MOUNT; outputInfo("status set to READY2MOUNT for S%dD%d, image: %s\n",_slot,_disk,ref.shortname.c_str()); } } need2remount=true; x_refresh_panel(PANEL_DISK); } return &ref; } const char* CEmulatorConfig::getSlot(int _slot, int _disk) { if (_slot<5 || _slot > 7 || _disk < 1 || _disk > MAXSLOT7DRIVES) return NULL; CActiveImage active = activeImages[_slot-5][_disk-1 ]; CSlotInfo& ref = localImages[_slot-5][_disk-1 ][active.iActive]; return STRING2CONSTCHAR(ref.url); } void CEmulatorConfig::processCommandLine(int argc, char** argv) { for(int i=1;i.activegsxml||]", ACTIVEGSVERSIONSTRFULL,MB_OK); ::PostQuitMessage(0); #endif } else if ((i+1)setSlot(5,1,value,ADD); else if (!strcasecmp(option,"slot52")) this->setSlot(5,2,value,ADD); else if (!strcasecmp(option,"slot61")) this->setSlot(6,1,value,ADD); else if (!strcasecmp(option,"slot62")) this->setSlot(6,2,value,ADD); else if (!strcasecmp(option,"slot71")) this->setSlot(7,1,value,ADD); else if (!strcasecmp(option,"slot72")) this->setSlot(7,2,value,ADD); else if (!strcasecmp(option,"BootSlot")) { int bs = atoi(value); this->bootslot = bs; } else if (!strcasecmp(option,"Speed")) { int s = atoi(value); this->speed=s; } else if (!strcasecmp(option,"EmulatorParam")) this->emulatorParam=value; else if (!strcasecmp(option,"SystemParam")) this->systemParam=value; else if (!strcasecmp(option,"BramParam")) this->bramParam=value; else if (!strcasecmp(option,"XMLConfig")) this->xmlconfig=value; else if (!strcasecmp(option,"state")) { CDownload dl(this->baseURL); // dl.setBaseURL(_path); MyString path; MyString shortn; dl.retrieveFile(value,path,shortn); this->stateFilename=path.c_str(); } } } else { // vérifie l'extension (mais strip le #) MyString extv(argv[i]); int l = extv.ReverseFind("#"); if (l>0) extv = extv.Left(l); const char* ext = getext(extv.c_str()); if ( !strcasecmp(ext,"activegsxml") || !strcasecmp(ext,"xml")) { this->xmlconfig = argv[i] ; } else { MyString path; MyString file; // CDownload dl; int slot = 0; // dl.deleteCachedFile(argv[i]); CDownload dl(this->baseURL); size_t size = dl.retrieveFile(argv[i],path,file); ext = getext(path.c_str()); if (!strcasecmp(ext,"hdv")) { slot = 7; } else if (!strcasecmp(ext,"2mg") || !strcasecmp(ext,"po")) { if (size> 819712) // OG Fix 2MG size recognitioN (CF. RASTAN 819264 & POMS 7 819712) slot = 7; else slot = 5; } else if ( !strcasecmp(ext,"bin") || !strcasecmp(ext,"dsk") || !strcasecmp(ext,"nib") || !strcasecmp(ext,"po") || !strcasecmp(ext,"do") ) slot = 6; if (slot) { setSlot(slot,1,path,REPLACECURRENT); // bootslot = slot; OG Laisse la valeur par défaut! xmlconfig.Empty(); } else outputInfo("Unknown extension .%s\n",ext); } } } } int CEmulatorConfig::processXML(const char*_path,simplexml* ptrconfig) { const char* ver = ptrconfig->property("version"); if (!ver || atoi(ver)!=2) { outputInfo("invalid xml version - xml config skipped\n"); return 0; } int child = ptrconfig->number_of_children(); for(int i=0;ichild(i); if (!strcasecmp(ptr->key(),"uid")) { const char* s = ptr->value(); uid = s; } else if (/*!fastConfig && */!strcasecmp(ptr->key(),"image")) { int slot = atoi(ptr->property("slot")); int disk = atoi(ptr->property("disk")); if ( ( ( slot==5 || slot==6 ) && ( disk==1 || disk==2) ) || ( (slot==7) && ( disk>=1 || disk<=MAXSLOT7DRIVES) ) ) { MyString absp ; const char* s = ptr->value(); int patch_addr = 0; const char* patch_value = NULL; for(int idisk=0;idisknumber_of_children();idisk++) { simplexml* ptrd = ptr->child(idisk); if (!strcmp(ptrd->key(),"name")) s = ptrd->value(); else if (!strcmp(ptrd->key(),"patch")) { const char* addr_str = ptrd->property("addr"); if (addr_str) { sscanf(addr_str,"%X",&patch_addr); patch_value = ptrd->value(); if (strlen(patch_value)&1) patch_value=NULL; // doit être pair } } } if (s) { // CDownload dl(this->baseURL); CDownload::makeAbsolutePath(s,_path,absp); CSlotInfo* pslot = setSlot(slot,disk,absp.c_str(),ADD); if (pslot && patch_value && patch_addr) { pslot->patch_addr = patch_addr; pslot->patch_value = patch_value; pslot->patch_value.MakeUpper(); } } } } else if (/*!fastConfig && */!strcasecmp(ptr->key(),"bootslot")) { int slot = atoi(ptr->value()); if (slot>=5 && slot<=7) { bootslot = slot; } } else if (!strcasecmp(ptr->key(),"runtime")) { const char* mode_str = ptr->property("mode"); if (mode_str) { enum mode_breakpoint mode = BRK_DISABLED; if (strchr(mode_str,'X')) mode = (enum mode_breakpoint)(mode|BRK_X); if (strchr(mode_str,'R')) mode = (enum mode_breakpoint)(mode|BRK_READ); if (strchr(mode_str,'W')) mode = (enum mode_breakpoint)(mode|BRK_WRITE); if (strchr(mode_str,'P')) mode = (enum mode_breakpoint)(mode|BRK_PATCH); if (strchr(mode_str,'B')) mode = (enum mode_breakpoint)(mode|BRK_STOP); if (strchr(mode_str,'S')) mode = (enum mode_breakpoint)(mode|BRK_SLOWSPEED); if (strchr(mode_str,'D')) mode = (enum mode_breakpoint)(mode|BRK_DEFAULTSPEED); if (strchr(mode_str,'M')) mode = (enum mode_breakpoint)(mode|BRK_MESSAGE); const char* addr_str = ptr->property("addr"); if (addr_str && !(strlen(addr_str)&1)) { word32 patch_addr; sscanf(addr_str,"%X",&patch_addr); const char* value_str = ptr->value(); if (value_str == NULL || !(strlen(addr_str)&1)) { int bp = nb_bps; if (bp != MAX_BREAK_POINTS) { const char* check = ptr->property("check"); memset(BPs[bp].check,0,MAX_CHECK); if (check!=NULL) { // printf("found check:%s\n",check); int i=0; while(i='a') c1 -= ('a'-10); else if (c1>='A') c1 -= ('A'-10); else c1 -= '0'; if (c2>='a') c2 -= ('a'-10); else if (c2>='A') c2 -= ('A'-10); else c2 -= '0'; BPs[bp].check[i++]= c1*16+c2; } } BPs[bp].addr= patch_addr; BPs[bp].value = value_str; BPs[bp].mode = mode; nb_bps++; } } } } } else if (!strcasecmp(ptr->key(),"speed")) { int sp = atoi(ptr->value()); if (sp>=0 && spkey(),"bramparam")) { const char* s = ptr->value(); xmlBramParam = s; } else if (!strcasecmp(ptr->key(),"emulatorparam")) { const char* s = ptr->value(); xmlEmulatorParam = s; } else if (!strcasecmp(ptr->key(),"systemparam")) { const char* s = ptr->value(); xmlSystemParam = s; } else if (!strcasecmp(ptr->key(),"name")) { const char* s = ptr->value(); name = s; } else if (!strcasecmp(ptr->key(),"desc")) { const char* s = ptr->value(); desc = s; } else if (!strcasecmp(ptr->key(),"publisher")) { const char* s = ptr->value(); publisher = s; } else if (!strcasecmp(ptr->key(),"year")) { const char* s = ptr->value(); year = s; } if (!strcasecmp(ptr->key(),"state")) { const char* s = ptr->value(); CDownload dl(_path); // ??? MyString path; MyString shortn; dl.retrieveFile(s,path,shortn); stateFilename=path.c_str(); } } return 1; } void CEmulatorConfig::loadXMLFile() { if (xmlconfig.IsEmpty() && !pXML) { xmlEmulatorParam.Empty(); xmlSystemParam.Empty(); xmlBramParam.Empty(); return ; } if (xmlAlreadyLoaded) { printf("discard xml...\n"); return ; } resetConfig(); xmlAlreadyLoaded = 1; // If XML already present, use preparsed structrue if (pXML) { processXML(baseURL.c_str(),pXML); return ; } MyString fullpath; MyString fulldir; //CDownload dl; CDownload::makeAbsolutePath(STRING2CONSTCHAR(xmlconfig),baseURL.c_str(),fullpath); getdir(fullpath.c_str(),fulldir); xmlconfig = fullpath.c_str() ; ActiveGSList list; if (!list.loadFromFile(STRING2CONSTCHAR(xmlconfig))) return ; simplexml* ptrconfig = NULL; if (list.elements.size()==1) ptrconfig = list.elements.at(0).pXML; else { CStdStringA xmlconfig2 = xmlconfig; int pos = xmlconfig2.ReverseFind("#id="); if (pos==-1) { printf("No key requested with .activegsxml list format\n"); return ; } MyString key = xmlconfig2.Mid(pos+4); for(unsigned int i=0;ibaseURL.c_str(),domain); str += "&d="; str += domain.c_str(); #ifdef ACTIVEGSPLUGIN CDownload dl; dl.GetFile(str.c_str(),NULL); #endif #endif */ } void CEmulator::setConfig(CEmulatorConfig* _config) { config = _config; sendStats(); config->loadXMLFile(); /* CDownload dl; dl.setBaseURL(config->baseURL.c_str()); */ } char read2MG(const char* ref,int block, int byte) { return ref[0x40+block*512+byte]; } int find2MG(const char* ref,int block, char header, const char* str) { int byte = 4; while (1) { char r = read2MG(ref,block,byte); if (!r) return 0; if (r==header) { int c=0; while(str[c]) { if (read2MG(ref,block,byte+c+1)!=str[c]) break; c++; } if (!str[c]) { // found! return block*512+byte; } } byte+=0x27; if (byte==0x1FF) { byte =4; block = read2MG(ref,block,2) + read2MG(ref,block,3)*256; if (!block) return 0; } } return 0; } int findSetStart(const char* buffer) { // start dir int sys = find2MG(buffer,2,(char)0xD6,"SYSTEM"); if (!sys) { outputInfo("cannot find System dir!"); return false; } int sysbloc = buffer[sys+0x40+0x11]+buffer[sys+0x40+0x12]*256; int setstart = find2MG(buffer,sysbloc,0x1D,"SETSTART.DATA"); if (!setstart) { outputInfo("cannot find setstart file"); return false; } return setstart; } bool CEmulator::setStartParam() { // prend le nom du fichier en 71 int slot = setStart.slot/10; int drive = setStart.slot%10; CSlotInfo& ref = getLocalIMGInfo(slot,drive); const char* filename= ref.filename.c_str(); //getLocalIMG(setStart.slot/10,setStart.slot%10); if (!filename) return false; FILE* f=fopen(filename,"rb"); if (!f) return false; // get file size fseek(f,0,SEEK_END); int filesize=ftell(f); char* buffer=(char*)malloc(filesize); fseek(f,0,SEEK_SET); fread(buffer,1,filesize,f); fclose(f); // int offset = findSetStart(buffer); if (!offset) { free(buffer); return false; } char *entry = buffer+offset+0x40; /* char *entry = buffer+setStart.addr; */ if ((entry[0]!=0x1D) || (strcmp(entry+1,"SETSTART.DATA")) ) { free(buffer); return false; } // buffer + 17 = block int blockpos = (entry[17]+entry[18]*256)*512+64; // buffer + 19 = size int size = setStart.file.length(); entry[0x15]=(size+3)&0xFF; entry[0x16]=((size+3)>>8)&0xFF; entry[0x17]=0; char* block = buffer+blockpos; memset(block,0,256); block[0]=size&0xFF; block[1]=(size>>8)&0xFF; strcpy(block+2,setStart.file.c_str()); block[size+2]=0x31; // ???? f=fopen(filename,"wb"); if (!f) { free(buffer); return false; } fseek(f,0,SEEK_SET); fwrite(buffer,1,filesize,f); fclose(f); free(buffer); struct param p; p.data = "SetStart"; p.value = setStart.file; p.hex = offset+0x40; //setStart.addr; addUniqueParam(p); // params.push_back(p); x_refresh_panel(PANEL_PARAMS); return true; } int CEmulator::acceptEvents() { return r_sim65816.get_state()==RUNNING; } void CEmulator::onEmulatorParamChanged(const char* _param) { if (!acceptEvents()) { outputInfo("### onEmulatorParamChanged Ignored"); return; } option.addEmulatorParam(_param); setEmulatorParam(_param); option.enableOptions(); } void CEmulator::onBootSlotChanged(int _bootslot) { if (!acceptEvents()) { outputInfo("### OnBootSlotChanged Ignored"); return; } config->bootslot = _bootslot; relaunchEmulator(); } void CEmulator::relaunchEmulator() { outputInfo("Trying to relaunch Emulator()\n"); if (this !=theEmulator) { outputInfo("Another instance was running in another process : killing it()\n"); delete theEmulator; } forceFocus(); if (bInThread) { outputInfo("Asking for current instance to restart\n"); r_sim65816.restart_emulator(); } else { outputInfo("reactivate emulator!\n"); launchEmulator(); } } void CEmulator::onSpeedChanged(int _speed, bool _ignoreAcceptEvents) { if (!_ignoreAcceptEvents && !acceptEvents()) { outputInfo("### onSpeedChanged Ignored"); return; } /* int sp = g_sim65816.set_limit_speed((speedenum)_speed); config->speed = sp; */ option.setIntValue(OPTION_SPEED,_speed); option.enableOptions(); forceFocus(); } void CEmulator::addUniqueParam(struct param _p) { int i; for(i=0;i params; vector::iterator v; v = params.begin(); while(v!=params.end()) { if (v->data == _p.data) { v->value = _p.value; v->hex = _p.hex; return ; } v++; } params.push_back(_p); */ } const char* currentBuild=TOSTRING(ACTIVEGSMAJOR)"." TOSTRING(ACTIVEGSMINOR) "." TOSTRING(ACTIVEGSBUILD); void CEmulator::checkAlerts() { static int bRunOnlyOnce=0; if (bRunOnlyOnce) return ; bRunOnlyOnce=1; #ifdef _DEBUG option.setValue(OPTION_LATESTVERSION,"5.1.4"); #endif if (!option.getIntValue(OPTION_DONOTSHOWUPDATEALERT)) { MyString newestBuild; option.getStrValue(newestBuild,OPTION_LATESTVERSION); MyString lastestUpdatedBuild; option.getStrValue(lastestUpdatedBuild,OPTION_LATESTUPDATECHECK); if ( !newestBuild.IsEmpty() && lastestUpdatedBuild.compare(newestBuild) && newestBuild.compare(currentBuild) ) { option.setValue(OPTION_LATESTUPDATECHECK,newestBuild); option.saveOptions(2); x_alert("A newer version (%s) of ActiveGS is available!\nCheck http://activegs.freetoolsassociation.com for more info.",newestBuild.c_str(),currentBuild); return ; } } } bool CEmulator::setParam(eParamType _type, const char* _data, const char* _value) { static const struct { const char* name; int* address; } variables[] = { // boot { "boot", (int*)0x28 }, // drive emulation { "driveemulation", &g_iwm.g_fast_disk_emul }, { "slow", (int*)0 }, { "fast", (int*)1 }, // slot { "slot6", (int*)0x26 }, { "drive", (int*)0 }, { "disk", (int*)0 }, { "card",(int*)1}, // columns { "columns", (int*)0x19 }, { "40", (int*)0 }, { "80", (int*)1 }, // speed { "forcespeed", (int*)0x20 }, { "normal",(int*)0}, { "fast",(int*)1}, { "zipspeed",&g_moremem.g_zipgs_reg_c05a}, // { "simulatespace",&g_adb.g_simulate_space}, { "emulate6502",&g_sim65816.g_6502_emulation }, { "apple2erom",&g_sim65816.g_use_apple2e_rom }, // bram { "font", (int*)0x1A }, { "background", (int*)0x1B }, { "border", (int*)0x1C }, { "black", (int*)0x00 }, { "dark-blue", (int*)0x02 }, { "dark-green", (int*)0x04 }, { "medium-blue", (int*)0x06 }, { "brown", (int*)0x08 }, { "light-gray", (int*)0x0A }, { "green", (int*)0x0C }, { "aquamarine", (int*)0x0E }, { "deep-red", (int*)0x01 }, { "purple", (int*)0x03 }, { "dark-gray", (int*)0x05 }, { "light-blue", (int*)0x07 }, { "orange", (int*)0x09 }, { "pink", (int*)0x0b }, { "yellow", (int*)0x0d }, { "white", (int*)0x0f }, { "language", (int*)0x29 }, { "keyboard", (int*)0x2a }, { "french", (int*)0x02 }, /* * $0 = USA Dvorak * $1 = UK USA * $2 = French USA * $3 = Danish USA * $4 = Spanish USA * $5 = Italian USA * $6 = German USA * $7 = Swedish */ { NULL, NULL } }; int value = -1000; int* data = 0; if (!strcasecmp(_data,"driveemulation")) data=0; int i=0; while(variables[i].name) { if (!strcasecmp(_data,variables[i].name)) { data = (int*)variables[i++].address; while(variables[i].name) { if (!strcasecmp(_value,variables[i].name)) { value = (intptr_t)(variables[i].address); break; } i++; } } else i++; } if (value==-1000) value=atoi(_value); if (!data) data=(int*)atoi(_data); if ( data && (value!=-1000)) { outputInfo("%s = %s (%d)\n",_data,_value,value); struct param p; p.data = _data; p.value = _value; p.hex = value; if (data==&g_moremem.g_zipgs_reg_c05a) { g_moremem.g_zipgs_reg_c05a = ((g_moremem.g_zipgs_reg_c05a&0x0F)|(value&0x0F)<<4); p.value.Format("%3d%%",(int)((16-((g_moremem.g_zipgs_reg_c05a>>4)&0x0F))*100/16)); } else if ((intptr_t)data > 256) // if (_type==EMULATOR) *data = value; else { // bram int idx = (intptr_t)data; if (idx>0 && idx<256) g_clock.g_bram_ptr[idx]=value; else printf("invalid bram index : %s (%d)\n",_data,idx); } addUniqueParam(p); } else { #ifdef _DEBUG outputInfo("unknown parameters (%s)(%s)\n",_data,_value); #endif } return true; } bool CEmulator::parseParam(const char* _param,eParamType _type) { // Format // data=value;data2=value; const char* run = _param; if (!run) return false; MyString data; MyString value; while(1) { // data data.Empty(); // skip ; while (*run == ';') run++; if (!*run) return false; // done while(*run!='=' && *run!=':') { if (!*run) return false; data += *run; run++; } run++; // passe le = value.Empty(); while(*run && *run!=';') value += *run++; if (!strcasecmp(data.c_str(),"setstart")) extractSetStartParam(value.c_str()); else { if (data[0]!='_') setParam(_type,data.c_str(),value.c_str()); } if (!*run) break; run++; } return true; } bool COption::addEmulatorParam(const char* _param) { const char* run = _param; if (!_param) return true; // printf("loading system param\n"); MyString data; MyString value; while(1) { // data data.Empty(); // ignore the initial _ (for backward compatibility) if (*run=='_') run++; while(*run!='=' && *run!=':') { if (!*run) return false; data += *run; run++; } run++; // passe le = value.Empty(); while(*run && *run!=';') value += *run++; // regarde si c'est une option option_id id = findByName(data.c_str()); if (id!=OPTION_ERROR) setValue(id,value.c_str(),1); /* if (data[0]=='_') { MyString data2; data2 = data.substr(1); option_id id = findByName(data2.c_str()); if (id==OPTION_ERROR) printf("could not process : %s %s\n",data2.c_str(),value.c_str()); else setValue(id,value.c_str(),1); } */ if (!*run) break; run++; } return true; } bool CEmulator::extractSetStartParam(const char* run) { // Format slot|file setStart.slot = 0; int slot; MyString data; while(*run!=':' && *run) data += *run++; slot = atoi(data.c_str()); if ( (slot!=51) && (slot!=52) && (slot!=71) && (slot!=72) ) return false; data.Empty(); while(*run) data += *run++; setStart.slot = slot; setStart.file = data; return true; } // CChildView void CEmulator::updateBramParameters() { if (!this) return ; parseParam(bramParam.c_str(),BRAM); x_refresh_panel(PANEL_PARAMS); } void CEmulator::setBramParam(const char* _p) { if (bramParam.IsEmpty()) bramParam = _p; else { bramParam += ';'; bramParam += _p; } } void CEmulator::ejectDisk(int _slot,int _drive) { if (!this) return ; CSlotInfo& ref = getLocalIMGInfo(_slot,_drive); if (ref.status == UNDEFINED) return ; ref.status = EJECTED ; outputInfo("status set to EJECTED for S%dD%d\n",_slot,_drive); config->need2remount=true; x_refresh_panel(PANEL_DISK); if(g_sim65816.g_enable_disk_sound) play_sound(SOUND_EJECTION); } void CEmulator::reloadDisk(int _slot,int _drive) { if (!this) return ; CSlotInfo& ref = getLocalIMGInfo(_slot,_drive); if ( (ref.status == EJECTED )|| (ref.status == FAILED ) ) { ref.status = READY2MOUNT; outputInfo("status set to READY2MOUNT for S%dD%d\n",_slot,_drive); config->need2remount=true; x_refresh_panel(PANEL_DISK); } } CEmulator::CEmulator(CEmulator** _parent) { if (theEmulator) x_fatal_exit("MULTIPLE INSTANCES"); bInThread = false; theEmulator= this; parent = _parent ; #ifdef ACTIVEGSPLUGIN pInstance = NULL; #endif theWindow = NULL; g_sim65816.halt_sim = 0; x=0; y=0; memset(lights,0,sizeof(lights)); memset(tracks,0,sizeof(tracks)); config = NULL; } #ifdef ACTIVEGSPLUGIN void CEmulator::setPluginInstance(void* _instance) { pInstance = _instance; } #endif void CEmulator::mountDisks() { config->need2remount=true; forceFocus(); } int CEmulator::theThread() { #ifdef TEST_RASTER x_test_raster(); #else if (!config) { x_fatal_exit("!!!MISSING CONFIG!!!\n"); return 0; } if (bInThread) { x_fatal_exit("THREAD ALREADY RUNNING\n"); return 0; } bInThread=true; outputInfo("emulator thread launched!\n"); while(1) { config->need2remount=true; mountDelayedDisk(); nbparams=0; bramParam.Empty(); // 1- OPTION PAR DEFAULT DE L'EMULATEUR // initialise les options par défault setStart.slot=0; g_sim65816.g_6502_emulation = 0; g_sim65816.g_use_apple2e_rom = 0; // paramètre auto! if (config->bootslot==-1) { // default config->bootslot=5; for(int b = 5;b<=7;b++) { const char* s = config->getSlot(b,1); if (s && s[0]) { printf("bootslot set to %d\n",b); config->bootslot=b; break; } } } switch(config->bootslot) { case 6: setEmulatorParam("driveemulation:slow;emulate6502:1;"); setBramParam("slot6:card;boot:6;border:black;background:black;font:white"); break; case 5: setBramParam("boot=5"); break; default: setEmulatorParam("driveemulation:fast;"); setBramParam("slot6:disk"); break; } // 1 la vitesse g_moremem.g_zipgs_reg_c05a = 0x0F; // MAX SPEED int sp = config->speed; if (sp == SPEED_AUTO) { if (config->bootslot==6) sp = SPEED_1MHZ; else sp = SPEED_GS; printf("speed auto set to %d\n",sp); } onSpeedChanged(sp,true); // 2 - LES OPTIONS DU FICHIERS DE CONF option.loadOptions(); // 3 - LES OPTIONS DU FICHIER XML CONFIG printf("*loading option from xml\n"); printf("EmulatorParam: %s\n",config->xmlEmulatorParam.c_str()); printf("BramParam: %s\n",config->xmlBramParam.c_str()); printf("SystemParam: %s\n",config->xmlSystemParam.c_str()); option.addEmulatorParam(config->xmlSystemParam.c_str()); setBramParam(config->xmlSystemParam.c_str()); setBramParam(config->xmlBramParam.c_str()); option.addEmulatorParam(config->xmlEmulatorParam.c_str()); setEmulatorParam(config->xmlEmulatorParam.c_str()); // 4 - LES OPTIONS JAVASCRIPT printf("*loading option from javascript\n"); option.addEmulatorParam(STRING2CONSTCHAR(config->systemParam)); setBramParam(STRING2CONSTCHAR(config->systemParam)); setBramParam(STRING2CONSTCHAR(config->bramParam)); setEmulatorParam(STRING2CONSTCHAR(config->emulatorParam)); option.addEmulatorParam(STRING2CONSTCHAR(config->emulatorParam)); option.enableOptions(); mountDisks(); g_adb.g_adb_repeat_delay = option.getIntValue(OPTION_ADBDELAY); g_adb.g_adb_repeat_rate = option.getIntValue(OPTION_ADBRATE); int ar = option.getIntValue(OPTION_AUDIORATE); if (ar) { if (ar<11025 || ar>44100) ar = 22100; g_sound.g_audio_enable = 1; g_sound.g_audio_rate = ar; } else g_sound.g_audio_enable = 0; int mem = option.getIntValue(OPTION_MEMORYSIZE); if (mem <=8 && mem>=0) mem *= 1024*1024; else if (mem>=128 && mem<=8*1024) { mem *= 1024; } else if (mem>=128*1024 && mem<=8*1024*1024) { } else mem = 2*1024*1024; g_sim65816.g_mem_size_exp = mem; if (config->bootslot!=6 && g_sim65816.g_use_apple2e_rom) { printf("disabling apple 2e rom (only works when booting from slot6\n"); g_sim65816.g_use_apple2e_rom = 0; } g_savestate.init(); if (config->stateFilename.IsEmpty()) { } else { printf("about to restore : %s\n",config->stateFilename.c_str()); g_savestate.restoreState(config->stateFilename.c_str()); } printf("*** notifying boot option ***\n"); x_refresh_panel(PANEL_BOOT); printf("*** launching Emulator Mainloop\n"); #ifndef MAC kegsmain(0,NULL); #else extern int macmain(int,char**); macmain(0,NULL); #endif g_savestate.shut(); joystick_shut(); if (!r_sim65816.restart_requested) break; else { outputInfo("*** Restarting emulator\n"); r_sim65816.restart_requested = 0; outputInfo("*** bRestarting\n"); setConfig(config); } } #endif // TEST_RASTER outputInfo("end of emulator thread\n"); bInThread=false; return 0; } void CEmulator::forceFocus() { // TODO OG } void CEmulator::readImageInfo(int _slot,int _drive, int _imagelen, int _imagestart, int _isprodos) { if (!this) return ; CSlotInfo& ref = getLocalIMGInfo(_slot,_drive); ref.size = _imagelen; MyString prefix("?"); if (_isprodos) //_info.track0_len!=-1) { char ptr[32]; memset(ptr,0,sizeof(ptr)); const char* filename = ref.filename.c_str(); if (filename) { FILE* f = fopen(filename,"rb"); if (f) { fseek(f,_imagestart+0x404,SEEK_SET); if (16==fread(ptr,1,16,f)) if ( ((ptr[0]&0xF0)==0xF0) ) { int len= ptr[0]&0x0F; ptr[len+1] = 0; prefix = "/"; prefix += (ptr+1); } fclose(f); } } } else prefix = "NON-PRODOS"; ref.prefix = prefix; } int CEmulator::getLocalMultipleIMGInfo(int _slot,int _drive,CSlotInfo& info, int _pos) { CActiveImage& active = config->getActiveImage(_slot,_drive); if (_pos>=active.iNb) return 0; info = config->getLocalImage(_slot,_drive,_pos); if (active.iActive==_pos) return 1; else return -1; } int CEmulator::mountDelayedDisk() { for(int slot=5;slot<=7;slot++) for(int disk=1;disk<=2;disk++) { CSlotInfo& ref = getLocalIMGInfo(slot,disk); if (ref.status==DELAYEDMOUNT) { outputInfo("status set to READY2MOUNT for S%dD%d\n",slot,disk); ref.status=READY2MOUNT; } } return 0; } bool CEmulator::remainingDelayedDisk() { for(int slot=5;slot<=7;slot++) for(int disk=1;disk<=2;disk++) { CSlotInfo& ref = getLocalIMGInfo(slot,disk); if (ref.status==DELAYEDMOUNT) return true; } return false; } int CEmulator::getSmartSwap() { int found = 0; for(int slot=5;slot<=6;slot++) { for(int disk=1;disk<=2;disk++) { CActiveImage& active = config->getActiveImage(slot,disk); if (active.iNb > 1) { if (found) { // multiple multiple : does not know how to handle return 0; } else found = slot*10+disk; } } } return found; } int CEmulator::smartSwap() { int smart = getSmartSwap(); if (!smart) { outputInfo("smart swap not available\n"); return 0; } return swapDisk(smart/10,smart%10); } int CEmulator::swapDisk(int _slot,int _drive, int pos ) { CActiveImage& active = config->getActiveImage(_slot,_drive); if (active.iNb <=1) { outputInfo("nothing to swap\n"); return 0; } CSlotInfo& ref = getLocalIMGInfo(_slot,_drive); if (ref.status != UNDEFINED) { ref.status = EJECTED ; outputInfo("status set to EJECTED for S%dD%d\n",_slot,_drive); } //ejectDisk(_slot,_drive); // passe à la suivante if (pos<0) pos = active.iActive+1; if (pos>=active.iNb) pos=0; active.iActive = pos; outputInfo("swapping to entry %d\n",pos); CSlotInfo& info = getLocalIMGInfo(_slot,_drive); if (!info.url.IsEmpty()) { outputInfo("status set to DELAYEDMOUNT for S%dD%d\n",_slot,_drive); info.status = DELAYEDMOUNT; info.delay = (int)(60*1.5); // 5s } config->need2remount=true; x_refresh_panel(PANEL_DISK); return 1; } CSlotInfo& CEmulator::getLocalIMGInfo(int _slot,int _drive) { CActiveImage& active = config->getActiveImage(_slot,_drive); return config->getLocalImage(_slot,_drive,active.iActive); } int CEmulator::mountImages() { if (!this) return 0; #if defined(ACTIVEGSPLUGIN) && !defined(DRIVER_IOS) checkAlerts(); #endif if (config->need2remount) { //outputInfo("mounting images...\n"); int ri = 0; for(int slot=5;slot<=7;slot++) { int maxdrive=2; if (slot==7) maxdrive=MAXSLOT7DRIVES; for(int drive=1;drive<=maxdrive;drive++) { ri |= loadDiskImage(/*name,*/slot,drive); if (r_sim65816.should_emulator_terminate()) return 0 ; // regarde si il faut patcher le disque systËme if (setStart.slot == (slot*10)+drive) setStartParam(); } } config->need2remount = (int)remainingDelayedDisk(); if (ri) x_refresh_panel(PANEL_DISK); return 1; } else return 0; } int CEmulator::loadDiskImage(int _slot,int _drive) { ASSERT(theEmulator==this); CSlotInfo& ref = getLocalIMGInfo(_slot,_drive); if (ref.status != READY2MOUNT) return 0; if (ref.url.IsEmpty()) { ref.status = NOTHING; outputInfo("status set to NOTHING for S%dD%d\n",_slot,_drive); ref.filename.Empty(); ref.shortname.Empty(); return 1; } MyString path; MyString shortname; CDownload dl(this->config->baseURL); #ifdef DRIVER_ANDROID dl.fromMainThread = false; #endif if (dl.retrieveFile(STRING2CONSTCHAR(ref.url),path,shortname)) { ref.filename = path ; ref.shortname = shortname ; ref.status = MOUNTED ; outputInfo("status set to MOUNTED for S%dD%d\n",_slot,_drive); //ref.delay = 1; ::showStatus("Image %s mounted [S%dD%d]",ref.shortname.c_str(),_slot,_drive); return 1 ; } else { ref.status = FAILED ; outputInfo("status set to FAILED for S%dD%d\n",_slot,_drive); ref.shortname = getfile(STRING2CONSTCHAR(ref.url)); return 1; } } void CEmulator::resetLocalIMG(void) { } // Interface const char* getLocalIMG(int _slot,int _drive) { CEmulator* emu = CEmulator::theEmulator; CSlotInfo& ref = emu->getLocalIMGInfo(_slot,_drive); if (ref.status == DELAYEDMOUNT) { ref.delay--; if (ref.delay<=0) { ref.status = READY2MOUNT; outputInfo("status set to READY2MOUNT for S%dD%d : image %s\n",_slot,_drive,ref.shortname.c_str()); } emu->config->need2remount=true; return NULL; } if (ref.status == MOUNTED) return ref.filename.c_str() ; else return NULL; } int mountImages() { return CEmulator::theEmulator->mountImages(); } void updateBramParameters(unsigned char* bramPtr) { return CEmulator::theEmulator->updateBramParameters(); } void readImageInfo(int _slot,int _drive, int _imagelen, int _imagestart, int _isprodos) { return CEmulator::theEmulator->readImageInfo(_slot,_drive,_imagelen,_imagestart,_isprodos); } /* void refreshInfo() { CEmulator* emu = CEmulator::theEmulator; if (!emu) return ; emu->infoRefresh(); } */ void disableConsole() { CEmulator* emu = CEmulator::theEmulator; if (!emu) return ; emu->disableConsole(); } int activeRead(char* buf) { CEmulator* emu = CEmulator::theEmulator; if (!emu) { buf[0]=0; return 0; } else return (emu->activeRead(buf)); } void COption::enableOptions() { r_sim65816.g_ignore_bad_acc = !getIntValue(OPTION_HALTONBADACC); r_sim65816.g_ignore_halts = !getIntValue(OPTION_HALTONHALTS); r_sim65816.g_halt_on_bad_read = getIntValue(OPTION_HALTONBADREAD); r_sim65816.set_rewind_enable(getIntValue(OPTION_ENABLEREWIND)); r_sim65816.set_color_mode( (colormodeenum)getIntValue(OPTION_COLORMODE)); r_sim65816.set_video_fx((videofxenum)getIntValue(OPTION_VIDEOFX)); g_joystick_type = getIntValue(OPTION_JOYSTICKMODE); #ifndef VIRTUALAPPLE g_sim65816.set_fixed_vblank( getIntValue(OPTION_FIXEDVBLANK),(speedenum)getIntValue(OPTION_SPEED) ); #endif g_sim65816.g_enable_disk_sound = getIntValue(OPTION_DISKSOUND); g_paddles.g_joystick_scale_factor_x = (getIntValue(OPTION_JOYXSCALE)*256)/1000; // 0x100 = 1 g_paddles.g_joystick_scale_factor_y = (getIntValue(OPTION_JOYYSCALE)*256)/1000; // 0x100 = 1 g_adb.g_simulate_space = getIntValue(OPTION_SIMULATESPACE); if (r_sim65816.get_state()==RUNNING) paddle_fixup_joystick_type(); #ifdef DRIVER_IOS extern int x_frame_rate; extern int x_lock_zoom; x_frame_rate = getIntValue(OPTION_FRAMERATE); #warning "TODO CHANGE FPS IN REALTIME" x_lock_zoom = getIntValue(OPTION_LOCKZOOM); #endif x_refresh_panel(PANEL_RUNTIME); } void CEmulator::setLight(int _motorOn,int _slot, int _drive, int _track) { int id=0; for(int slot=5;slot<=6;slot++) for(int drive=1; drive<=2; drive++) { if ( (_slot==slot) && (_drive==drive) ) { lights[id]=_motorOn; tracks[id]=_track; } else lights[id]=0; id++; } } //extern char targetSpeed[256]; extern char estimatedSpeed[256]; void CEmulator::processCommand( const char* _val, MyString& ret) { // outputInfo("processingCommand:%s\n",_val); if (!strcmp(_val,"help")) ret = "({ commands : [ \"help\" , \"version\", \"system\", \"status\", \"runtime\", \"reboot\",\"savestate\", \"restorestate\", \"pause\", \"resume\" ,\"pauseresume\", \"swap\", \"screenshot\",\"saveoptions\",\"defaultoptions\",\"sendkey:ctrl;alt;esc\" ] })"; else if (!strcasecmp(_val,"saveoptions")) { option.saveOptions(1); ret = "({ \"info\" : \"options saved\"})"; } else if (!strcasecmp(_val,"defaultOptions")) { option.setDefaultOptions(); option.enableOptions(); ret = "({ \"info\" : \"default options restored\"})"; } else if (!strncasecmp(_val,"sendkey:",8 /* sizeof ("sendkey:")*/)) { #if defined (DRIVER_OSX) && !defined(ACTIVEGSPLUGIN) ret = "({ \"info\" : \"not implemented\"})"; #else const char* keystr[] = { "^ctrl" , "^alt", "^esc", "^reset", "^option", NULL }; const int keycode[] = { 0x36 , 0x37 , 0x35, 0x7F, 0x3A, 0 }; #define MAX_CODE 100 int code[MAX_CODE]; int nbcode=0; const char* ptr = _val+8; MyString keysent; while(*ptr) { const char* p = ptr; while(*p && *p!=';') p++; int l = p-ptr; int k = 0; while(keystr[k]) { if (!strncasecmp(keystr[k],ptr,l)) { code[nbcode++]=k; // printf("found %s (%X)\n",keystr[k],keycode[k]); keysent+=keystr[k]; keysent+=";"; break; } k++; } if (!keystr[k]) printf("key not valid %s\n",ptr); ptr = p; if (*ptr==';') ptr++; if (nbcode==MAX_CODE) break; } for(int i=0;i=0;i--) { add_event_key(keycode[code[i]],1); } ret.Format("({ \"info\" : \"key sent %s\"})",keysent.c_str()); #endif } else if (!strcasecmp(_val,"version")) { ret = "({ \"info\" : \"" ACTIVEGSVERSIONSTRFULL "\"})"; } else /* if (!strcmp(_val,"rewind")) { stateActionRequired = STATE_REWIND; ret = "({ \"info\" : \"rewind activated\"})"; } else */ if (!strcasecmp(_val,"system")) { ret = "( { "; ret += " \"currentBuild\" : \"" TOSTRING(ACTIVEGSMAJOR) "." TOSTRING(ACTIVEGSMINOR) "." TOSTRING(ACTIVEGSBUILD) "\","; MyString str; int g_joystick_plugged = (g_joystick_native_type1 != -1) || (g_joystick_native_type2 != -1) ? 1 : 0 ; str.Format("\"joystickPlugged\" : %d,",g_joystick_plugged); ret += str.c_str(); MyString engine; getEngineString(engine); str.Format("\"renderingEngine\" : \"%s\",",engine.c_str()); ret += str.c_str(); str.Format("\"memorySize\" : %d,",option.getIntValue(OPTION_MEMORYSIZE)); ret += str.c_str(); str.Format("\"audioRate\" : %d",option.getIntValue(OPTION_AUDIORATE)); ret += str.c_str(); ret +="})"; } else if (!strcasecmp(_val,"reboot")) { // Force la relecture du fichier XML! config->xmlAlreadyLoaded = 0; onBootSlotChanged(-1); // Bootslot Auto ret = "({ \"info\" : \"Rebooted!\" })"; } else if (!strcasecmp(_val,"status")) { MyString str; ret = "( {"; str.Format("\"vbl\" : %d,",g_sim65816.g_vbl_count); ret += str; str.Format("\"speed\" : { estimated:\"%s\", target:\"",estimatedSpeed); if (!g_sim65816.g_speed_fast) str +="1"; else str += option.getDescription(OPTION_SPEED,g_sim65816.get_limit_speed()); str +="\"},"; ret += str; ret += " \"lights\" : {"; for(int i=0;i<4;i++) { str.Format(" \"slot%d%d\" : { \"on\" : %d, \"track\" : %d }",(i/2)+5,(i&1)+1,lights[i],tracks[i]); ret += str; if (i!=3) ret+=","; } ret += "}"; ret += "} )"; } else if (!strcasecmp(_val,"runtime")) { MyString str; ret = "( {"; str.Format("\"pause\" : %d,",r_sim65816.get_state()==IN_PAUSE); ret += str; str.Format("\"swap\" : %d,",getSmartSwap()); ret += str; str.Format("\"rewindAvailable\" : %d,",r_sim65816.get_rewind_enable()); ret += str.c_str(); str.Format("\"savedState\" : %d,", g_savestate.getSavedStateVBL()); ret += str.c_str(); ret += "\"slots\" : ["; int nb = 0; for(int slot=5;slot<=7;slot++) { int maxdrive=2; if (slot==7) maxdrive=MAXSLOT7DRIVES; for(int disk=1;disk<=maxdrive;disk++) { for(int active=0;activegetLocalIMGInfo(sw/10,sw%10); ret = info.shortname; ret.Format("({ \"info\" : \"disk %s inserted\", \"slot\" : %d, name : \"%s\" })",info.shortname.c_str(),sw,info.shortname.c_str()); } } else { ret="({ \"error\" : \"unsupported command:"; ret += _val ; ret += "\"})"; } } // initialize le son depuis le thread principal void CEmulator::launchEmulator() { x_async_sound_init(); //CDownload::initPersistentPath(); x_LaunchThread(); // attend la fin de l'émulateur // x_WaitForEndThread(); } void CEmulator::terminateEmulator() { outputInfo("terminateCEmulator (%X)\n",this); if (bInThread) { // envoie le signal de quit r_sim65816.quit_emulator(); // attend la fin de l'émulateur x_WaitForEndThread(); x_async_snd_shutdown(); } // l'émulateur a quitté correctement : il est pret à redémarrer r_sim65816.reset_quit(); // OG 080111 Force un redraw pour rafraichir le contenu du browser si besoin refresh_video(1); } CEmulator::~CEmulator() { outputInfo("~CEmulator (%X)",this); // termine le singleton theEmulator=NULL; if (parent) *parent = NULL; } void CEmulator::onXMLConfigChanged(const char *_xmlconfig,simplexml* _pXML) { if (!acceptEvents()) { outputInfo("### onXMLConfigChanged Ignored"); return; } MyString newxmlconfig = _xmlconfig; // le copie si la valeur était déjà stockée dans xmlconfig // config->resetConfig(); // remets les valeur par défault config->xmlconfig = newxmlconfig.c_str(); config->pXML = _pXML; config->xmlAlreadyLoaded=0; // pour forcer la relecture au prochain chargement relaunchEmulator(); } extern void addNotificationWindow(); void addConsoleWindow(Kimage* _dst) { #if defined(_DEBUG) && defined(WIN32) && 0 extern void addConsole(const char*); // extern char estimatedSpeed[256]; extern void addConsole(const char* _str); // extern int getFrameRate(int _id); extern void drawConsole(Kimage* _dst); char str[256]; int fr0 = getFrameRate(0); int fr1 = getFrameRate(1); int fr2 = getFrameRate(2); int fr3 = getFrameRate(3); sprintf(str,"fps:%02d.%0d 60hz:%02d.%0d req:%02d.%0d c054:%02d.%0d ",fr0/10,fr0%10,fr3/10,fr3%10,fr1/10,fr1%10,fr2/10,fr2%10); // sprintf(str,"p1:%d p2:%d fps:%d ",getFrameRate(3),getFrameRate(4),getFrameRate(1)); addConsole(str); drawConsole(_dst); #endif // addNotificationWindow(); } #ifdef TEST_RASTER int x_test_raster() { x_video_init(); r_sim65816.set_state(RUNNING); while(!r_sim65816.should_emulator_terminate()) { static int pos=0; // faire scroller // int bitmapBytesPerRow = (pixelsWide * 4);// 1 int bitmapBytesPerRow = g_kimage_offscreen.width_act * (g_kimage_offscreen.mdepth >> 3); int destx=0; int desty = 0; int col = pos; x_wait_for_last_refresh(1.0/60.0); CHANGE_BORDER(0,0x00FF00); byte* ptrdest = (byte*)g_kimage_offscreen.data_ptr ; for(int i=0;i> 3); int destx= g_kimage_offscreen.width_act - NOTIFICATION_WIDTH - NOTIFICATION_MARGIN ; int col = 0xFFFFFF; for(int i=0;i=g_kimage_offscreen.height) continue; byte* ptrdest = (byte*)g_kimage_offscreen.data_ptr + y*bitmapBytesPerRow; for(int j=0;jg_kimage_offscreen.width_act ) ) continue; ((word32*)ptrdest)[x] = col; } } if (g_notification_motion) { g_notification_y += g_notification_speed; g_notification_speed += 0.5f; if (g_notification_y>= g_kimage_offscreen.height-(NOTIFICATION_HEIGHT+NOTIFICATION_MARGIN)) { g_notification_y = (float)(g_kimage_offscreen.height-(NOTIFICATION_HEIGHT+NOTIFICATION_MARGIN)); if (g_notification_speed<2.0f) { g_notification_motion = 0; g_notification_show = 3*60; } else g_notification_speed *= -0.5f; } } else { g_notification_show--; } // g_notification_y++; if (g_notification_y>g_kimage_offscreen.height+NOTIFICATION_HEIGHT) g_notification_y = -NOTIFICATION_HEIGHT; g_video.g_needfullrefreshfornextframe = 1; }