Merge pull request #2 from ghedger/master
Added modifier keys to F2 reset to prevent accidental reboots when switc...
Tim O'Brien
10 years ago
Binary diff not shown
62 | 62 | // |
63 | 63 | /////////////////////////////////////////////////////////// |
64 | 64 | |
65 | #define MAX_OUTPUT 0x7fff | |
65 | // GPH Changed from 0x7fff: we want some headroom to add in the Apple speaker | |
66 | // in the mixing phase and can avoid an unnecessary divide with the | |
67 | // associated problems by simply picking smaller values. | |
68 | // Note also this number can theoretically go as high as 0x3fff without | |
69 | // causing post-mix clipping, but even that sounds too loud... | |
70 | #define MAX_OUTPUT 0x0fff | |
66 | 71 | |
67 | 72 | // See AY8910_set_clock() for definition of STEP |
68 | 73 | #define STEP 0x8000 |
123 | 123 | //static signed long g_uInternalExecutedCycles; |
124 | 124 | // TODO: Use IRQ_CHECK_TIMEOUT=128 when running at full-speed else with IRQ_CHECK_TIMEOUT=1 |
125 | 125 | // - What about when running benchmark? |
126 | static const int IRQ_CHECK_TIMEOUT = 128; | |
126 | // GPH Dropped to IRQ_CHECK_TIMOUT=16 - Mockingboard-intensive applications sound | |
127 | // "jerky" at 128. Does not show appreciable CPU impact in top CPU profiler. | |
128 | static const int IRQ_CHECK_TIMEOUT = 16; | |
127 | 129 | static signed int g_nIrqCheckTimeout = IRQ_CHECK_TIMEOUT; |
128 | 130 | |
129 | 131 | // |
155 | 155 | } |
156 | 156 | #else |
157 | 157 | /* Windows specific functions of reading directory structure */ |
158 | /* Find subdirs: */ | |
158 | /* Find subdirs: */ | |
159 | 159 | if(strcmp(incoming_dir, "/")) { |
160 | 160 | // we are not in upper direcory |
161 | 161 | tmp = new char[3]; |
167 | 167 | B = 1; |
168 | 168 | } |
169 | 169 | else B = 0; // for sorting dirs |
170 | ||
170 | ||
171 | 171 | |
172 | 172 | WIN32_FIND_DATA finfo; |
173 | 173 | HANDLE h; |
174 | 174 | |
175 | ||
176 | ||
175 | ||
176 | ||
177 | 177 | h=FindFirstFile(incoming_dir,&finfo); |
178 | 178 | |
179 | 179 | if (h!=INVALID_HANDLE_VALUE) { |
197 | 197 | strcpy(tmp, "<DIR>"); |
198 | 198 | sizes.Add(tmp); // add sign of directory |
199 | 199 | } |
200 | } /* while */ | |
201 | } /* if */ | |
200 | } /* while */ | |
201 | } /* if */ | |
202 | 202 | |
203 | 203 | #endif |
204 | 204 | // sort directories. Please, don't laugh at my bubble sorting - it the simplest thing I've ever seen --bb |
240 | 240 | (void) closedir (dp); |
241 | 241 | #else |
242 | 242 | /* Windows specific functions of reading directory structure */ |
243 | /* Find files: */ | |
243 | /* Find files: */ | |
244 | 244 | |
245 | 245 | h=FindFirstFile(incoming_dir,&finfo); |
246 | 246 | |
252 | 252 | strcpy(tmp,finfo.cFileName); |
253 | 253 | files.Add(tmp); |
254 | 254 | tmp = new char[10]; // 1400000KB |
255 | snprintf(tmp, 9, "%dKB", | |
255 | snprintf(tmp, 9, "%dKB", | |
256 | 256 | ((finfo.nFileSizeHigh * (MAXDWORD+1)) + finfo.nFileSizeLow)); |
257 | 257 | sizes.Add(tmp); // add this size to list |
258 | 258 | } |
263 | 263 | strcpy(tmp,finfo.cFileName); |
264 | 264 | files.Add(tmp); |
265 | 265 | tmp = new char[10]; // 1400000KB |
266 | snprintf(tmp, 9, "%dKB", | |
266 | snprintf(tmp, 9, "%dKB", | |
267 | 267 | ((finfo.nFileSizeHigh * (MAXDWORD+1)) + finfo.nFileSizeLow)); |
268 | 268 | sizes.Add(tmp); // add this size to list |
269 | 269 | } |
270 | } /* while */ | |
271 | } /* if */ | |
270 | } /* while */ | |
271 | } /* if */ | |
272 | 272 | |
273 | 273 | #endif |
274 | 274 | // do sorting for files |
308 | 308 | } |
309 | 309 | else tempSurface = g_origscreen; |
310 | 310 | |
311 | if(tempSurface == NULL) | |
311 | if(tempSurface == NULL) | |
312 | 312 | tempSurface = screen; // use screen, if none available |
313 | ||
313 | ||
314 | 314 | my_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, tempSurface->w, tempSurface->h, tempSurface->format->BitsPerPixel, 0, 0, 0, 0); |
315 | 315 | if(tempSurface->format->palette && my_screen->format->palette) |
316 | 316 | SDL_SetColors(my_screen, tempSurface->format->palette->colors, |
358 | 358 | SDL_Rect r; |
359 | 359 | r.x= 2; |
360 | 360 | r.y= TOPX + (i-first_file) * 15 * facy - 1; |
361 | if(strlen(tmp) > 46) r.w = 46 * 6 * 1.7 * facx + 2; | |
362 | else r.w= strlen(tmp) * 6 * 1.7 * facx + 2; // 6- FONT_SIZE_X | |
361 | if(strlen(tmp) > 46) r.w = 46 * FONT_SIZE_X /* 6 */ * 1.7 * facx + 2; | |
362 | else r.w= strlen(tmp) * FONT_SIZE_X /* 6 */ * 1.7 * facx + 2; // 6- FONT_SIZE_X | |
363 | 363 | r.h= 9 * 1.5 * facy; |
364 | 364 | SDL_FillRect(screen, &r, SDL_MapRGB(screen->format,255,0,0));// in RED |
365 | 365 | } /* if */ |
398 | 398 | |
399 | 399 | // control cursor |
400 | 400 | keyboard = SDL_GetKeyState(NULL); // get current state of pressed (and not pressed) keys |
401 | ||
401 | 402 | if (keyboard[SDLK_UP] || keyboard[SDLK_LEFT]) { |
402 | 403 | if (act_file>0) act_file--; // up one position |
403 | 404 | if (act_file<first_file) first_file=act_file; |
550 | 550 | char * |
551 | 551 | md5str (const char *input) |
552 | 552 | { |
553 | static char result[16 * 3 +1]; | |
553 | char result[16 * 3 +1]; | |
554 | 554 | unsigned char* digest = (unsigned char*)md5 (input); |
555 | 555 | int i; |
556 | 556 |
391 | 391 | 0, 256); |
392 | 392 | // Uint32 myblack = SDL_MapRGB(screen->format, 0, 0, 0); // black color |
393 | 393 | // SDL_SetColorKey(g_hStatusSurface,SDL_SRCCOLORKEY/* | SDL_RLEACCEL*/, myblack); |
394 | ||
394 | ||
395 | 395 | if (drawflags & DRAW_BACKGROUND) |
396 | 396 | { |
397 | 397 | /* Code moved to Video.cpp in CreateDIBSections() |
493 | 493 | "Conf file is linapple.conf in current directory by default", |
494 | 494 | "Hugest archive of Apple][ stuff you can find at ftp.apple.asimov.net", |
495 | 495 | " F1 - This help", |
496 | " F2 - Cold reset, Shift+F2 - Reload conf file and restart", | |
496 | " Ctrl+Shift+F2 - Cold reset", | |
497 | " Shift+F2 - Reload conf file and restart", | |
497 | 498 | " F3, F4 - Choose an image file name for floppy disk", |
498 | 499 | " in Slot 6 drive 1 or 2 respectively", |
499 | 500 | " Shift+F3, Shift+F4 - The same thing for Apple hard disks", |
942 | 943 | break; |
943 | 944 | |
944 | 945 | case BTN_RUN: // F2 - Run that thing! Or Shift+2 ReloadConfig and run it anyway! |
945 | if(mod & KMOD_SHIFT) { | |
946 | if((mod & (KMOD_LCTRL|KMOD_LSHIFT)) == (KMOD_LCTRL|KMOD_LSHIFT) || | |
947 | (mod & (KMOD_RCTRL|KMOD_RSHIFT)) == (KMOD_RCTRL|KMOD_RSHIFT)) { | |
948 | if (g_nAppMode == MODE_LOGO) | |
949 | DiskBoot(); | |
950 | else if (g_nAppMode == MODE_RUNNING) | |
951 | ResetMachineState(); | |
952 | if ((g_nAppMode == MODE_DEBUG) || (g_nAppMode == MODE_STEPPING)) | |
953 | DebugEnd(); | |
954 | g_nAppMode = MODE_RUNNING; | |
955 | DrawStatusArea(/*(HDC)0,*/DRAW_TITLE); | |
956 | VideoRedrawScreen(); | |
957 | g_bResetTiming = true; | |
958 | } | |
959 | else if(mod & KMOD_SHIFT) { | |
946 | 960 | restart = 1; // keep up flag of restarting |
947 | 961 | qe.type = SDL_QUIT; |
948 | 962 | SDL_PushEvent(&qe);// push quit event |
949 | } | |
950 | else { | |
951 | if (g_nAppMode == MODE_LOGO) | |
952 | DiskBoot(); | |
953 | else if (g_nAppMode == MODE_RUNNING) | |
954 | ResetMachineState(); | |
955 | if ((g_nAppMode == MODE_DEBUG) || (g_nAppMode == MODE_STEPPING)) | |
956 | DebugEnd(); | |
957 | g_nAppMode = MODE_RUNNING; | |
958 | DrawStatusArea(/*(HDC)0,*/DRAW_TITLE); | |
959 | VideoRedrawScreen(); | |
960 | g_bResetTiming = true; | |
961 | } | |
963 | } | |
962 | 964 | break; |
963 | 965 | |
964 | 966 | case BTN_DRIVE1: |
965 | 967 | case BTN_DRIVE2: |
966 | 968 | if (mod & KMOD_SHIFT) { |
969 | if(mod & KMOD_ALT) | |
970 | HD_FTP_Select(button - BTN_DRIVE1);// select HDV image through FTP | |
971 | else | |
972 | HD_Select(button - BTN_DRIVE1); // select HDV image from local disk | |
973 | } else { | |
967 | 974 | if(mod & KMOD_ALT) |
968 | HD_FTP_Select(button - BTN_DRIVE1);// select HDV image through FTP | |
969 | else HD_Select(button - BTN_DRIVE1); // select HDV image from local disk | |
970 | } | |
971 | else { | |
972 | if(mod & KMOD_ALT) Disk_FTP_SelectImage(button - BTN_DRIVE1);//select through FTP | |
973 | else DiskSelect(button - BTN_DRIVE1); // select image file for appropriate disk drive(#1 or #2) | |
975 | Disk_FTP_SelectImage(button - BTN_DRIVE1);//select through FTP | |
976 | else | |
977 | DiskSelect(button - BTN_DRIVE1); // select image file for appropriate disk drive(#1 or #2) | |
974 | 978 | } |
975 | 979 | /* if (!fullscreen) |
976 | 980 | DrawButton((HDC)0,button);*/ |
182 | 182 | |
183 | 183 | static const DWORD g_dwDSBufferSize = 16 * 1024 * sizeof(short) * g_nMB_NumChannels; |
184 | 184 | |
185 | static const SHORT nWaveDataMin = (SHORT)0x8000; | |
186 | static const SHORT nWaveDataMax = (SHORT)0x7FFF; | |
185 | static const SHORT nWaveDataMin = (SHORT)0xF000; | |
186 | static const SHORT nWaveDataMax = (SHORT)0x0FFF; | |
187 | 187 | |
188 | 188 | static short g_nMixBuffer[g_dwDSBufferSize / sizeof(short)]; |
189 | 189 |
130 | 130 | //std::cerr << "DEBUG wanted: " << wantedSamples |
131 | 131 | // << " actual: " << audioSpec.size / 4 << std::endl; |
132 | 132 | frequency = audioSpec.freq; |
133 | bufferSize = 4 * (audioSpec.size / sizeof(short)); | |
133 | bufferSize = 8 * (audioSpec.size / sizeof(short)); | |
134 | 134 | // bufferSize = SPKR_SAMPLE_RATE * 2 * sizeof(short); // 1 second of stereo short data |
135 | 135 | |
136 | 136 | /* |
249 | 249 | ////////////////////////////////////////////////////////////////////// |
250 | 250 | ////////////////////////////////////////////////////////////////////// |
251 | 251 | |
252 | ||
253 | // GPH this is called on IRQ to refresh the audio at regular intervals. | |
254 | // We'll mix the buffers here keeping in mind the need for speed. | |
255 | // | |
252 | 256 | void audioCallback(short* stream, unsigned len) |
253 | 257 | { |
254 | 258 | int i; |
255 | 259 | static short lastvalue = 0; |
256 | ||
257 | 260 | assert((len & 1) == 0); // stereo |
261 | ||
258 | 262 | unsigned len1, len2; |
259 | 263 | unsigned available = getBufferFilled(); |
260 | ||
261 | //std::cerr << "DEBUG callback: " << available << std::endl; | |
262 | 264 | unsigned num = std::min(len, available); |
263 | 265 | if ((readIdx + num) < bufferSize) { |
266 | // No split in source (mixBuffer); perform straight-up copy. | |
267 | // | |
268 | // |--------------------| | |
269 | // ^ (mixBuffer) $ | |
270 | // | |
271 | // |--------------------| | |
272 | // ^ (stream) | |
264 | 273 | memcpy(stream, &mixBuffer[readIdx], num * sizeof(short)); |
265 | 274 | readIdx += num; |
266 | 275 | } else { |
276 | // Handle split in source | |
277 | // | |
278 | // |--------------------| | |
279 | // (mixBuffer) ^ | |
280 | // | |
281 | // |--------------------| | |
282 | // ^ (stream) | |
267 | 283 | len1 = bufferSize - readIdx; |
268 | 284 | memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short)); |
269 | 285 | len2 = num - len1; |
271 | 287 | readIdx = len2; |
272 | 288 | } |
273 | 289 | |
290 | // Fill the remainer of the buffer with last value to prevent potential | |
291 | // clicks and pops. | |
274 | 292 | if (available != 0) { |
275 | 293 | if (readIdx != 0) |
276 | 294 | lastvalue = mixBuffer[readIdx-1]; |
279 | 297 | } |
280 | 298 | |
281 | 299 | for (i = num; i < len; i++) { |
282 | stream[i] = lastvalue; | |
300 | stream[i] = lastvalue; | |
283 | 301 | } |
284 | ||
285 | /* Note: cleared at the begininng of the func | |
286 | ||
287 | int missing = len - available; | |
288 | if (missing > 0) { | |
289 | // buffer underrun | |
290 | //std::cerr << "DEBUG underrun: " << missing << std::endl; | |
291 | memset(&stream[available], 0, missing * sizeof(short)); | |
292 | } | |
293 | */ | |
302 | /* GPH please don't do this in an IRQ handler! | |
294 | 303 | unsigned target = (5 * bufferSize) / 8; |
295 | 304 | double factor = double(available) / target; |
296 | 305 | filledStat = (63 * filledStat + factor) / 64; |
297 | //std::cerr << "DEBUG filledStat: " << filledStat << std::endl; | |
306 | */ | |
307 | ||
298 | 308 | #ifdef MOCKINGBOARD |
299 | // And add Mockingboard sound data to the stream | |
309 | // And add Mockingboard sound data to the stream | |
310 | // GPH: We are going to add the Mockingboard and speaker samples. | |
311 | // Their independent maximum amplitudes have been selected to eliminate | |
312 | // any possibility of wave peak clipping. This speeds up the timing-sensitive | |
313 | // operation here (since we're in an IRQ handler) and eliminates the | |
314 | // need for a potentially expensive divide. | |
300 | 315 | available = getBuffer2Filled(); |
301 | 316 | //std::cerr << "DEBUG callback: " << available << std::endl; |
302 | 317 | num = std::min(len, available); |
318 | const short *pSrc; | |
319 | short *pDest = stream; | |
303 | 320 | if ((readIdx2 + num) < bufferSize) { |
304 | // memcpy(stream, &mixBuffer[readIdx], num * sizeof(short)); | |
305 | for(i = 0; i < num; i++) | |
306 | stream[i] |= mockBuffer[readIdx2 + i]; | |
307 | readIdx2 += num; | |
321 | if( num ) { | |
322 | pSrc = &mockBuffer[readIdx2]; | |
323 | readIdx2 += num; | |
324 | while(num--) { | |
325 | *pDest += *pSrc; | |
326 | pDest++; pSrc++; | |
327 | } | |
328 | } | |
308 | 329 | } else { |
309 | len1 = bufferSize - readIdx2; | |
310 | // memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short)); | |
311 | for(i = 0; i < len1; i++) | |
312 | stream[i] |= mockBuffer[readIdx2 + i]; | |
330 | // We crossed the "seam" on the circular mockBuffer. | |
331 | // We will therefore perform two copies, the segmentation being determined | |
332 | // by the split in the source buffer (mockBuffer). | |
333 | // | |
334 | // |--------------------| | |
335 | // (mockBuffer) ^ | |
336 | // | |
337 | // | |
338 | // |--------------------| | |
339 | // ^ (stream) | |
340 | len1 = bufferSize - readIdx2; | |
313 | 341 | len2 = num - len1; |
314 | // memcpy(&stream[len1], mixBuffer, len2 * sizeof(short)); | |
315 | for(i = 0; i < len2; i++) | |
316 | stream[len1 + i] |= mockBuffer[i]; | |
342 | if(len1) { | |
343 | pSrc = &mockBuffer[readIdx2]; | |
344 | while(len1--) { | |
345 | *pDest += *pSrc; | |
346 | pDest++; pSrc++; | |
347 | } | |
348 | } | |
317 | 349 | readIdx2 = len2; |
318 | } | |
319 | #endif | |
350 | if(len2) { | |
351 | pSrc = mockBuffer; | |
352 | while(len2--) { | |
353 | *pDest += *pSrc; | |
354 | pDest++; pSrc++; | |
355 | } | |
356 | } | |
357 | ||
358 | } | |
359 | #endif // #ifdef MOCKINGBOARD | |
320 | 360 | // normalization |
361 | // GPH TODO: Rather than do this in this handler, we should | |
362 | // perform it efficiently by changing the values of | |
363 | // MAX_OUTPUT (AY8910.cpp) and SPKR_DATA_INIT (Speaker.cpp), | |
364 | // making them variables and making the appropriate access functions | |
365 | // to call from Frame.cpp | |
321 | 366 | /* const short MY_MAX_VOLUME = (short)SDL_MIX_MAXVOLUME / 2; |
322 | 367 | for(unsigned k=0;k<len;k+=2) |
323 | 368 | if((short)stream[k] > MY_MAX_VOLUME) |
49 | 49 | //------------------------------------- |
50 | 50 | |
51 | 51 | // Globals (SOUND_WAVE) |
52 | const short SPKR_DATA_INIT = (short)0x8000; // data written to speakers buffer | |
53 | ||
54 | static short g_nSpeakerData = SPKR_DATA_INIT; | |
52 | // GPH This "INIT" value is the actual sample data written to the speaker when | |
53 | // it gets whapped with a BIT $C030. Note the output is always digital. | |
54 | // The Apple II had no notion of volume or of looping waves, only the single | |
55 | // "click" of the speaker. We set this to a value well under the maximum to | |
56 | // provide headroom for mixing Mockingboard sound without causing peak clipping. | |
57 | const short SPKR_DATA_INIT = (short)0x2000; // data written to speakers buffer | |
58 | ||
59 | static short g_nSpeakerData = 0x0000; //SPKR_DATA_INIT; | |
55 | 60 | static short *g_pSpeakerBuffer = NULL; |
56 | static UINT g_nBufferIdx = 0; | |
61 | static UINT g_nBufferIdx = 0; | |
57 | 62 | |
58 | 63 | static short *g_pStereoBuffer = NULL; // buffer for stereo samples |
59 | 64 | |
60 | ||
61 | 65 | static short *g_pRemainderBuffer = NULL; // Remainder buffer |
62 | static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize() | |
63 | static UINT g_nRemainderBufferIdx; // Setup in SpkrInitialize() | |
64 | ||
66 | static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize() | |
67 | static UINT g_nRemainderBufferIdx; // Setup in SpkrInitialize() | |
65 | 68 | |
66 | 69 | // Application-wide globals: |
67 | DWORD soundtype = SOUND_WAVE; //default | |
70 | DWORD soundtype = SOUND_WAVE; //default | |
68 | 71 | double g_fClksPerSpkrSample; // Setup in SetClksPerSpkrSample() |
69 | 72 | |
70 | 73 | // Globals |
300 | 303 | } |
301 | 304 | |
302 | 305 | g_nSpkrLastCycle = g_nCumulativeCycles; |
303 | } | |
304 | ||
305 | //============================================================================= | |
306 | ||
307 | // Called by emulation code when Speaker I/O reg is accessed | |
306 | ||
307 | // GPH Added - simulate decoupling capacitor - use approximate value | |
308 | g_nSpeakerData = (short)((double) g_nSpeakerData * 0.995); | |
309 | } | |
310 | ||
311 | //============================================================================= | |
312 | ||
313 | // Called by emulation code when Speaker I/O reg (0xC030) is accessed | |
308 | 314 | BYTE SpkrToggle (WORD, WORD, BYTE, BYTE, ULONG nCyclesLeft) |
309 | 315 | { |
310 | 316 | g_bSpkrToggleFlag = true; |
326 | 332 | |
327 | 333 | UpdateSpkr(); |
328 | 334 | |
329 | g_nSpeakerData = ~g_nSpeakerData; | |
335 | g_nSpeakerData = g_nSpeakerData ^ SPKR_DATA_INIT; | |
330 | 336 | } |
331 | 337 | |
332 | 338 | return MemReadFloatingBus(nCyclesLeft); // reading from $C030..$C03F retrurns unpredictable value? |
85 | 85 | /* ---------------------- FONT routines ---------------------------*/ |
86 | 86 | /* ----------------------------------------------------------------*/ |
87 | 87 | |
88 | #define FONT_SIZE_X 6 | |
88 | #define FONT_SIZE_X 7 | |
89 | 89 | #define FONT_SIZE_Y 8 |
90 | 90 | // chars in row in font bitmap |
91 | #define CHARS_IN_ROW 45 | |
91 | #define CHARS_IN_ROW 39 | |
92 | 92 | extern SDL_Surface *font_sfc; |
93 | 93 | |
94 | 94 | bool fonts_initialization(void); |