// ===================================================================================== // Copyright (c) 2022 Dave Bernazzani (wavemotion-dave) // // Copying and distribution of this emulator, it's source code and associated // readme files, with or without modification, are permitted in any medium without // royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition), // Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely. // // A7800DS emulator is offered as-is, without any warranty. // ===================================================================================== #include #include #include #include #include #include #include "main.h" #include "config.h" #include "a7800utils.h" #include "emu/Database.h" #include "emu/ProSystem.h" #include "clickNoQuit_wav.h" #include "bgBottom.h" #include "bgTop.h" #include "bgFileSel.h" #include "printf.h" #include "soundbank.h" #include "soundbank_bin.h" u8 isDS_LITE __attribute__((section(".dtcm"))) = 0; u8 frameSkipMask __attribute__((section(".dtcm"))) = 1; u16 lastSample __attribute__((section(".dtcm"))) = 0; u16 gTotalAtariFrames __attribute__((section(".dtcm"))) = 0; int atari_frames __attribute__((section(".dtcm"))) = 0; u8 bRefreshXY __attribute__((section(".dtcm"))) = false; u16 dampen __attribute__((section(".dtcm"))) = 0; unsigned char keyboard_data[20] __attribute__((section(".dtcm"))) ALIGN(32); u16 full_speed __attribute__((section(".dtcm"))) = 0; short int emu_state __attribute__((section(".dtcm"))) = 0; u16 fpsDisplay __attribute__((section(".dtcm"))) = 0; u16 bEmulatorRun __attribute__((section(".dtcm"))) = 1; extern u32 tiaBufIdx; char fpsbuf[33]; // ----------------------------------------------------------------- // Some vars for listing filenames of ROMs... 1K of ROMs is plenty // ----------------------------------------------------------------- FICA7800 proromlist[1024]; unsigned int countpro=0, countfiles=0, ucFicAct=0; int bg0; int bg1; int bg0b,bg1b; int bg2; int bg3; // BG pointers int bg0s, bg1s; // sub BG pointers #define MAX_DEBUG 6 int debug[MAX_DEBUG]={0}; u8 DEBUG_DUMP = 0; uint video_height; // Actual video height u16 *bufVideo; // Video flipping buffer gamecfg GameConf; // Game Config svg short cxBG __attribute__((section(".dtcm"))); short cyBG __attribute__((section(".dtcm"))); short xdxBG __attribute__((section(".dtcm"))); short ydyBG __attribute__((section(".dtcm"))); u32 myTiaBufIdx __attribute__((section(".dtcm"))) = 0; u8 soundEmuPause = 1; #define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); static void DumpDebugData(void) { if (DEBUG_DUMP) { for (int i=0; i= 0) { sprintf(fpsbuf, "ILLOP=%02X", last_illegal_opcode); dsPrintValue(24,21,0, fpsbuf); } } } // ------------------------------------------------------------ // Utility function to pause the sound... // ------------------------------------------------------------ void SoundPause(void) { soundEmuPause = 1; } // ------------------------------------------------------------ // Utility function to un pause the sound... // ------------------------------------------------------------ void SoundUnPause(void) { myTiaBufIdx = 0; tiaBufIdx = 0; memset(tia_buffer, 0x00, SNDLENGTH); TIMER0_CR=0; TIMER0_DATA=0; TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; atari_frames = 0; WAITVBL;WAITVBL; soundEmuPause = 0; } // Color fading effect void FadeToColor(unsigned char ucSens, unsigned short ucBG, unsigned char ucScr, unsigned char valEnd, unsigned char uWait) { unsigned short ucFade; unsigned char ucBcl; // Fade-out vers le noir if (ucScr & 0x01) REG_BLDCNT=ucBG; if (ucScr & 0x02) REG_BLDCNT_SUB=ucBG; if (ucSens == 1) { for(ucFade=0;ucFadevalEnd;ucFade--) { if (ucScr & 0x01) REG_BLDY=ucFade; if (ucScr & 0x02) REG_BLDY_SUB=ucFade; for (ucBcl=0;ucBcl0 ? "<" : " ")); dsPrintValue(31,22,0,(char *) (NoDebGame+14" : " ")); sprintf(szName,"%s","A=SELECT, Y=HALT EMU, B=BACK"); dsPrintValue(16-strlen(szName)/2,23,0,szName); for (u8 ucBcl=0;ucBcl<17; ucBcl++) { ucGame= ucBcl+NoDebGame; if (ucGame < countpro) { maxLen=strlen(proromlist[ucGame].filename); strcpy(szName,proromlist[ucGame].filename); if (maxLen>29) szName[29]='\0'; if (proromlist[ucGame].directory) { sprintf(szName2,"%-29s",szName); dsPrintValue(0,5+ucBcl,(ucSel == ucBcl ? 1 : 0),szName2); } else { sprintf(szName2,"%-29s",strupr(szName)); dsPrintValue(1,5+ucBcl,(ucSel == ucBcl ? 1 : 0),szName2); } } } } unsigned int dsWaitForRom(void) { bool bDone=false, bRet=false; u32 ucHaut=0x00, ucBas=0x00,ucSHaut=0x00, ucSBas=0x00,romSelected= 0, firstRomDisplay=0,nbRomPerPage, uNbRSPage, uLenFic=0; s32 ucFlip=0, ucFlop=0; decompress(bgFileSelTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(bgFileSelMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); dmaCopy((void *) bgFileSelPal,(u16*) BG_PALETTE_SUB,256*2); unsigned short dmaVal = *(bgGetMapPtr(bg1b) +31*32); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); nbRomPerPage = (countpro>=17 ? 17 : countpro); uNbRSPage = (countpro>=5 ? 5 : countpro); if (ucFicAct>countpro-nbRomPerPage) { firstRomDisplay=countpro-nbRomPerPage; romSelected=ucFicAct-countpro+nbRomPerPage; } else { firstRomDisplay=ucFicAct; romSelected=0; } dsDisplayFiles(firstRomDisplay,romSelected); while (!bDone) { if (keysCurrent() & KEY_UP) { ucFlip = -50; ucFlop = 0; uLenFic = 0; if (!ucHaut) { ucFicAct = (ucFicAct>0 ? ucFicAct-1 : countpro-1); if (romSelected>uNbRSPage) { romSelected -= 1; } else { if (firstRomDisplay>0) { firstRomDisplay -= 1; } else { if (romSelected>0) { romSelected -= 1; } else { firstRomDisplay=countpro-nbRomPerPage; romSelected=nbRomPerPage-1; } } } ucHaut=0x01; dsDisplayFiles(firstRomDisplay,romSelected); } else { ucHaut++; if (ucHaut>10) ucHaut=0; } } else { ucHaut = 0; } if (keysCurrent() & KEY_DOWN) { ucFlip = -50; ucFlop = 0; uLenFic = 0; if (!ucBas) { ucFicAct = (ucFicAct< countpro-1 ? ucFicAct+1 : 0); if (romSelected10) ucBas=0; } } else { ucBas = 0; } if((keysCurrent() & KEY_R) || (keysCurrent() & KEY_RIGHT)) { if (!ucSBas) { ucFicAct = (ucFicAct< countpro-nbRomPerPage ? ucFicAct+nbRomPerPage : countpro-nbRomPerPage); if (firstRomDisplay10) ucSBas=0; } } else { ucSBas = 0; } if ((keysCurrent() & KEY_L) || (keysCurrent() & KEY_LEFT)) { if (!ucSHaut) { ucFicAct = (ucFicAct> nbRomPerPage ? ucFicAct-nbRomPerPage : 0); if (firstRomDisplay>nbRomPerPage) { firstRomDisplay -= nbRomPerPage; } else { firstRomDisplay = 0; } if (ucFicAct == 0) romSelected = 0; if (romSelected > ucFicAct) romSelected = ucFicAct; ucSHaut=0x01; dsDisplayFiles(firstRomDisplay,romSelected); } else { ucSHaut++; if (ucSHaut>10) ucSHaut=0; } } else { ucSHaut = 0; } if ( keysCurrent() & KEY_B ) { bDone=true; while (keysCurrent() & KEY_B); } if (keysCurrent() & (KEY_A | KEY_Y)) { if (!proromlist[ucFicAct].directory) { if (keysCurrent() & KEY_Y) bEmulatorRun = false; else bEmulatorRun=true; if (keysCurrent() & KEY_X) DEBUG_DUMP = 1; else DEBUG_DUMP=0; bRet=true; bDone=true; } else { chdir(proromlist[ucFicAct].filename); proFindFiles(); ucFicAct = 0; nbRomPerPage = (countpro>=16 ? 16 : countpro); uNbRSPage = (countpro>=5 ? 5 : countpro); if (ucFicAct>countpro-nbRomPerPage) { firstRomDisplay=countpro-nbRomPerPage; romSelected=ucFicAct-countpro+nbRomPerPage; } else { firstRomDisplay=ucFicAct; romSelected=0; } dsDisplayFiles(firstRomDisplay,romSelected); while (keysCurrent() & KEY_A); } } // Scroll la selection courante if (strlen(proromlist[ucFicAct].filename) > 29) { ucFlip++; if (ucFlip >= 20) { ucFlip = 0; uLenFic++; if ((uLenFic+29)>strlen(proromlist[ucFicAct].filename)) { ucFlop++; if (ucFlop >= 20) { uLenFic=0; ucFlop = 0; } else uLenFic--; } strncpy(szName,proromlist[ucFicAct].filename+uLenFic,29); szName[29] = '\0'; dsPrintValue(1,5+romSelected,1,szName); } } swiWaitForVBlank(); } decompress(bgBottomTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(bgBottomMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); dmaCopy((void *) bgBottomPal,(u16*) BG_PALETTE_SUB,256*2); dmaVal = *(bgGetMapPtr(bg1b) +31*32); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); return bRet; } unsigned int dsWaitOnMenu(unsigned int actState) { unsigned int uState=A7800_PLAYINIT; bool bDone=false, romSel; int iTx,iTy; while (bDone==false) { // wait for stylus scanKeys(); if(keysHeld() & KEY_TOUCH) { touchPosition touch; touchRead(&touch); iTx = touch.px; iTy = touch.py; if ((iTx>31) && (iTx<65) && (iTy>159) && (iTy<169)) { // 32,160 -> 64,168 quit soundPlaySample(clickNoQuit_wav, SoundFormat_16Bit, clickNoQuit_wav_size, 22050, 127, 64, false, 0); bDone=dsWaitOnQuit(); if (bDone) uState=A7800_QUITSTDS; } if ((iTx>69) && (iTx<180) && (iTy>10) && (iTy<65)) { // 80,32 -> 179,61 cartridge slot bDone=true; // Find files in current directory and show it proFindFiles(); romSel=dsWaitForRom(); if (romSel) { uState=A7800_PLAYINIT; dsLoadGame(proromlist[ucFicAct].filename); } else { uState=actState; } } } swiWaitForVBlank(); } return uState; } void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr) { u16 *pusEcran,*pusMap; u16 usCharac; char *pTrTxt=pchStr; char ch; pusEcran=(u16*) (bgGetMapPtr(bg1b))+x+(y<<5); pusMap=(u16*) (bgGetMapPtr(bg0b)+(2*isSelect+24)*32); while((*pTrTxt)!='\0' ) { ch = *pTrTxt; if (ch >= 'a' && ch <= 'z') ch -= 32; // Faster than strcpy/strtoupper usCharac=0x0000; if ((ch) == '|') usCharac=*(pusMap); else if (((ch)<' ') || ((ch)>'_')) usCharac=*(pusMap); else if((ch)<'@') usCharac=*(pusMap+(ch)-' '); else usCharac=*(pusMap+32+(ch)-'@'); *pusEcran++=usCharac; pTrTxt++; } } // -------------------------------------------------------------------------------------------- // MAXMOD streaming setup and handling... // -------------------------------------------------------------------------------------------- #define sample_rate 31400 // To rough match the TIA driver for the Atari 7800 - we purposely undershoot slightly #define buffer_size (256) // Enough buffer that we don't have to fill it too often but not so big as to create lag mm_ds_system sys __attribute__((section(".dtcm"))); mm_stream myStream __attribute__((section(".dtcm"))); // ------------------------------------------------------------------------------------------- // maxmod will call this routine when the buffer is half-empty and requests that // we fill the sound buffer with more samples. They will request 'len' samples and // we will fill exactly that many. If the sound is paused, we fill with 'mute' samples. // ------------------------------------------------------------------------------------------- ITCM_CODE mm_word OurSoundMixer(mm_word len, mm_addr dest, mm_stream_formats format) { if (soundEmuPause) // If paused, just send same value - no amplitude... no sound { s32 *p = (s32*)dest; for (int i=0; i>1); while (new_len--) { if (myTiaBufIdx != tiaBufIdx) { *p++ = tia_buffer[myTiaBufIdx++]; myTiaBufIdx &= (SNDLENGTH-1); } else // We're short some samples... this happens due to emualtor timing not being perfect. Just make up the samples. { *p++ = (myCartInfo.pokeyType ? pokey_ProcessNow() : tia_ProcessNow()); } } } return len; } // ------------------------------------------------------------------------------------------- // Setup the maxmod audio stream - this will be an 8-bit Mono output. // ------------------------------------------------------------------------------------------- void setupStream(void) { //---------------------------------------------------------------- // initialize maxmod with our small 3-effect soundbank //---------------------------------------------------------------- mmInitDefaultMem((mm_addr)soundbank_bin); mmLoadEffect(SFX_CLICKNOQUIT); mmLoadEffect(SFX_KEYCLICK); mmLoadEffect(SFX_MUS_INTRO); //---------------------------------------------------------------- // open stream //---------------------------------------------------------------- myStream.sampling_rate = sample_rate; // sampling rate = myStream.buffer_length = buffer_size; // buffer length = myStream.callback = OurSoundMixer; // set callback function myStream.format = MM_STREAM_8BIT_MONO; // format = mono 8-bit myStream.timer = MM_TIMER2; // use hardware timer 2 myStream.manual = false; // use automatic filling mmStreamOpen( &myStream ); //---------------------------------------------------------------- // when using 'automatic' filling, your callback will be triggered // every time half of the wave buffer is processed. // // so: // 25000 (rate) // ----- = ~21 Hz for a full pass, and ~42hz for half pass // 1200 (length) //---------------------------------------------------------------- // with 'manual' filling, you must call mmStreamUpdate // periodically (and often enough to avoid buffer underruns) //---------------------------------------------------------------- } //--------------------------------------------------------------------------------- void dsInstallSoundEmuFIFO(void) { setupStream(); } // ---------------------------------------------------------------------------------- // This is where the action happens! The main loop runs continually and clocks // out the 60 frames per second of the 7800 Prosystem // ---------------------------------------------------------------------------------- ITCM_CODE void dsMainLoop(void) { static u8 lcd_swap_counter=0; static u8 special_hsc_entry=0; static short int last_keys_pressed = 999; unsigned int keys_pressed,keys_touch=0, romSel; short iTx,iTy; static int scale_screen_dampen=0; // Timers are fed with 33.513982 MHz clock. // With DIV_1024 the clock is 32,728.5 ticks per sec... TIMER0_DATA=0; TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; TIMER1_DATA=0; TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024; while(emu_state != A7800_QUITSTDS) { switch (emu_state) { case A7800_MENUINIT: dsShowScreenMain(true); emu_state = A7800_MENUSHOW; break; case A7800_MENUSHOW: emu_state = dsWaitOnMenu(A7800_MENUSHOW); break; case A7800_PLAYINIT: dsShowScreenEmu(); emu_state = A7800_PLAYGAME; break; case A7800_PLAYGAME: // 32,728.5 ticks = 1 second // 1 frame = 1/50 or 1/60 (0.02 or 0.016) // 655 -> 50 fps and 546 -> 60 fps if (!full_speed) { while(TIMER0_DATA < (546*atari_frames)) ; } if (bEmulatorRun) { // Execute one frame prosystem_ExecuteFrame(keyboard_data); if (++atari_frames == 60) { TIMER0_CR=0; TIMER0_DATA=0; TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; atari_frames=0; } // Read keys if (special_hsc_entry > 0) { special_hsc_entry--; tchepres(10); if (special_hsc_entry < 10) { tchepres(11); } continue; } memset(keyboard_data, 0x00, 15); // Not the difficulty switches which are the two bytes after this... } scanKeys(); keys_pressed = keysCurrent(); if (dampen == 0) { // if touch screen pressed if (keys_pressed & KEY_TOUCH) { touchPosition touch; touchRead(&touch); iTx = touch.px; iTy = touch.py; if ((iTx>8) && (iTx<55) && (iTy>154) && (iTy<171)) { // 32,160 -> 64,168 POWER SoundPause(); mmEffect(SFX_KEYCLICK); // Play short key click for feedback... if (dsWaitOnQuit()) emu_state=A7800_QUITSTDS; else SoundUnPause(); } else if ((iTx>240) && (iTx<256) && (iTy>0) && (iTy<20)) { // Full Speed Toggle ... upper corner... full_speed = 1-full_speed; if (full_speed) dsPrintValue(30,0,0,"FS"); else dsPrintValue(30,0,0," "); dampen=60; } else if ((iTx>63) && (iTx<105) && (iTy>154) && (iTy<171)) { // 72,160 -> 105,168 PAUSE if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback... tchepres(10); } else if ((iTx>152) && (iTx<198) && (iTy>154) && (iTy<171)) { // 142,160 -> 175,168 SELECT if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback... tchepres(11); } else if ((iTx>208) && (iTx<251) && (iTy>154) && (iTy<171)) { // 191,160 -> 224,168 RESET if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback... tchepres(6); } else if ((iTx>90) && (iTx<110) && (iTy>90) && (iTy<110)) { // Atari Logo - Activate HSC Maintenence Mode (only on High Score screen) special_hsc_entry=70; } else if ((iTx>115) && (iTx<144) && (iTy>154) && (iTy<171)) { // Snap HSC Sram dsPrintValue(13,0,0, "SAVING"); mmEffect(SFX_KEYCLICK); // Play short key click for feedback... WAITVBL;WAITVBL; cartridge_SaveHighScoreSram(); dsPrintValue(13,0,0, " "); dampen=60; continue; } else if ((iTx>69) && (iTx<180) && (iTy>22) && (iTy<62)) // Cartridge slot { SoundPause(); // Find files in current directory and show it proFindFiles(); romSel=dsWaitForRom(); if (romSel) { emu_state=A7800_PLAYINIT; dsLoadGame(proromlist[ucFicAct].filename); if (full_speed) dsPrintValue(30,0,0,"FS"); else dsPrintValue(30,0,0," ");} else { SoundUnPause(); } } else if ((iTx>190) && (iTx<230) && (iTy>22) && (iTy<62)) // Gear Icon (Settings) { SoundPause(); ShowConfig(); SoundUnPause(); } keys_touch=1; } else keys_touch=0; if (keys_pressed != last_keys_pressed) { last_keys_pressed = keys_pressed; if ( (keys_pressed & KEY_SELECT) ) { tchepres(11); } // BUTTON SELECT if (myCartInfo.cardctrl1 != TWIN) { if ( (keys_pressed & KEY_START) ) {tchepres(10);} // BUTTON PAUSE if ( (keys_pressed & KEY_X) ) { fpsDisplay = 1-fpsDisplay; gTotalAtariFrames=0; if (!fpsDisplay) dsPrintValue(0,0,0," ");} } if (myCartInfo.cardctrl1 == SOTA) { if ( (keys_pressed & KEY_R) ) { myCartInfo.xOffset +=28; bRefreshXY = true; } if ( (keys_pressed & KEY_L) ) { myCartInfo.xOffset -=28; bRefreshXY = true; } } if (dampen < 6) dampen = 6; } } else dampen--; // manage a7800 pad if ( (keys_pressed & KEY_UP) ) { tchepres(0); } // UP if ( (keys_pressed & KEY_DOWN) ) { tchepres(1); } // DOWN if ( (keys_pressed & KEY_RIGHT) ) { tchepres(3); } // RIGHT if ( (keys_pressed & KEY_LEFT) ) { tchepres(2); } // LEFT if (myCartInfo.cardctrl1 == TWIN) { if ( (keys_pressed & KEY_A) ) { tchepres(12); } // Left Joystick Right if ( (keys_pressed & KEY_B) ) { tchepres(13); } // Left Joystick Down if ( (keys_pressed & KEY_X) ) { tchepres(15); } // Left Joystick Up if ( (keys_pressed & KEY_Y) ) { tchepres(14); } // Left Joystick Left if ( (keys_pressed & KEY_START) ) { tchepres(4); } // Fire Button (mainly to enter high scores and start game) } else { if ( (keys_pressed & KEY_A) ) { tchepres(4); } // BUTTON #1 if ( (keys_pressed & KEY_B) ) { tchepres(5); } // BUTTON #2 if ( (keys_pressed & KEY_Y) ) { tchepres(4); } // BUTTON #1 } if ((keys_pressed & KEY_R) || (keys_pressed & KEY_L)) { if ((keys_pressed & KEY_R) && (keys_pressed & KEY_L)) { if (++lcd_swap_counter == 30) { if (keys_pressed & KEY_A) lcdSwap(); } } if (scale_screen_dampen > 5) { if ((keys_pressed & KEY_R) && (keys_pressed & KEY_UP)) { myCartInfo.yOffset++; bRefreshXY = true; } if ((keys_pressed & KEY_R) && (keys_pressed & KEY_DOWN)) { myCartInfo.yOffset--; bRefreshXY = true; } if ((keys_pressed & KEY_R) && (keys_pressed & KEY_LEFT)) { myCartInfo.xOffset++; bRefreshXY = true; } if ((keys_pressed & KEY_R) && (keys_pressed & KEY_RIGHT)) { myCartInfo.xOffset--; bRefreshXY = true; } if ((keys_pressed & KEY_L) && (keys_pressed & KEY_UP)) if (myCartInfo.yScale <= 256) { myCartInfo.yScale++; bRefreshXY = true; } if ((keys_pressed & KEY_L) && (keys_pressed & KEY_DOWN)) if (myCartInfo.yScale > 192) { myCartInfo.yScale--; bRefreshXY = true; } if ((keys_pressed & KEY_L) && (keys_pressed & KEY_RIGHT)) if (myCartInfo.xScale < 320) { myCartInfo.xScale++; bRefreshXY = true; } if ((keys_pressed & KEY_L) && (keys_pressed & KEY_LEFT)) if (myCartInfo.xScale > 192) { myCartInfo.xScale--; bRefreshXY = true; } scale_screen_dampen=0; } else scale_screen_dampen++; } else lcd_swap_counter=0; // ------------------------------------------------------------- // Stuff to do once/second such as FPS display and Debug Data // ------------------------------------------------------------- if (TIMER1_DATA >= 32728) // 1000MS (1 sec) { TIMER1_CR = 0; TIMER1_DATA = 0; TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024; if (fpsDisplay) { int fps = gTotalAtariFrames; if (fps == 61 && !full_speed) fps=60; gTotalAtariFrames = 0; static u8 lastFPS=99; if (fps != lastFPS) { lastFPS = fps; fpsbuf[0] = '0' + (int)fps/100; fps = fps % 100; fpsbuf[1] = '0' + (int)fps/10; fpsbuf[2] = '0' + (int)fps%10; fpsbuf[3] = 0; dsPrintValue(0,0,0, fpsbuf); } } DumpDebugData(); } break; } } prosystem_Close(); } //---------------------------------------------------------------------------------- // Find files (a78 / bin) available int a78Filescmp (const void *c1, const void *c2) { FICA7800 *p1 = (FICA7800 *) c1; FICA7800 *p2 = (FICA7800 *) c2; if (p1->filename[0] == '.' && p2->filename[0] != '.') return -1; if (p2->filename[0] == '.' && p1->filename[0] != '.') return 1; if (p1->directory && !(p2->directory)) return -1; if (p2->directory && !(p1->directory)) return 1; return strcasecmp (p1->filename, p2->filename); } char filenametmp[255]; void proFindFiles(void) { DIR *pdir; struct dirent *pent; countpro = countfiles = 0; pdir = opendir("."); if (pdir) { while (((pent=readdir(pdir))!=NULL)) { strcpy(filenametmp,pent->d_name); if (pent->d_type == DT_DIR) { if (!( (filenametmp[0] == '.') && (strlen(filenametmp) == 1))) { proromlist[countpro].directory = true; strcpy(proromlist[countpro].filename,filenametmp); countpro++; } } else { if (strlen(filenametmp)>4) { if ( (strcasecmp(strrchr(filenametmp, '.'), ".a78") == 0) ) { proromlist[countpro].directory = false; strcpy(proromlist[countpro].filename,filenametmp); countpro++;countfiles++; } if ( (strcasecmp(strrchr(filenametmp, '.'), ".bin") == 0) ) { proromlist[countpro].directory = false; strcpy(proromlist[countpro].filename,filenametmp); countpro++;countfiles++; } } } } closedir(pdir); } if (countpro) { qsort (proromlist, countpro, sizeof (FICA7800), a78Filescmp); } else // Failsafe... always provide a back directory... { proromlist[countpro].directory = true; strcpy(proromlist[countpro].filename,".."); countpro = 1; } } // End of file