void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnQuit(JNIEnv *env, jobject obj) { #if TESTING // test driver thread is managing CPU #else appState = APP_REQUESTED_SHUTDOWN; LOG("..."); disk6_eject(0); disk6_eject(1); cpu_resume(); #endif }
TEST test_cputrace_hello_dsk() { test_setup_boot_disk(BLANK_DSK, 0); BOOT_TO_DOS(); const char *homedir = HOMEDIR; char *output = NULL; asprintf(&output, "%s/a2_cputrace_hello_dsk.txt", homedir); if (output) { unlink(output); cpu65_trace_begin(output); } srandom(0); apple_ii_64k[0][WATCHPOINT_ADDR] = 0x00; test_type_input("RUN HELLO\r"); c_debugger_go(); cpu65_trace_end(); disk6_eject(0); do { uint8_t md[SHA_DIGEST_LENGTH]; char mdstr0[(SHA_DIGEST_LENGTH*2)+1]; FILE *fp = fopen(output, "r"); fseek(fp, 0, SEEK_END); long expectedSize = ftell(fp); ASSERT(expectedSize == EXPECTED_CPUTRACE_HELLO_FILE_SIZE); fseek(fp, 0, SEEK_SET); unsigned char *buf = malloc(EXPECTED_CPUTRACE_HELLO_FILE_SIZE); if (fread(buf, 1, EXPECTED_CPUTRACE_HELLO_FILE_SIZE, fp) != EXPECTED_CPUTRACE_HELLO_FILE_SIZE) { ASSERT(false); } fclose(fp); fp = NULL; SHA1(buf, EXPECTED_CPUTRACE_HELLO_FILE_SIZE, md); FREE(buf); sha1_to_str(md, mdstr0); ASSERT(strcmp(mdstr0, EXPECTED_CPUTRACE_HELLO_SHA) == 0); } while(0); unlink(output); FREE(output); PASS(); }
TEST test_boot_disk_vmtrace_po() { test_setup_boot_disk(BLANK_PO, 0); const char *homedir = HOMEDIR; char *disk = NULL; asprintf(&disk, "%s/a2_vmtrace_po.txt", homedir); if (disk) { unlink(disk); vm_trace_begin(disk); } srandom(0); BOOT_TO_DOS(); vm_trace_end(); disk6_eject(0); do { uint8_t md[SHA_DIGEST_LENGTH]; char mdstr0[(SHA_DIGEST_LENGTH*2)+1]; FILE *fp = fopen(disk, "r"); fseek(fp, 0, SEEK_END); long expectedSize = ftell(fp); ASSERT(expectedSize == EXPECTED_VM_TRACE_PO_FILE_SIZE); fseek(fp, 0, SEEK_SET); unsigned char *buf = malloc(EXPECTED_VM_TRACE_PO_FILE_SIZE); if (fread(buf, 1, EXPECTED_VM_TRACE_PO_FILE_SIZE, fp) != EXPECTED_VM_TRACE_PO_FILE_SIZE) { ASSERT(false); } fclose(fp); fp = NULL; SHA1(buf, EXPECTED_VM_TRACE_PO_FILE_SIZE, md); FREE(buf); sha1_to_str(md, mdstr0); ASSERT(strcmp(mdstr0, EXPECTED_VM_TRACE_PO_SHA) == 0); } while(0); unlink(disk); FREE(disk); PASS(); }
TEST test_boot_disk_cputrace() { const char *homedir = HOMEDIR; char *output = NULL; asprintf(&output, "%s/a2_cputrace.txt", homedir); if (output) { unlink(output); cpu65_trace_begin(output); } srandom(0); BOOT_TO_DOS(); cpu65_trace_end(); disk6_eject(0); do { uint8_t md[SHA_DIGEST_LENGTH]; char mdstr0[(SHA_DIGEST_LENGTH*2)+1]; FILE *fp = fopen(output, "r"); fseek(fp, 0, SEEK_END); long expectedSize = ftell(fp); ASSERT(expectedSize == EXPECTED_CPU_TRACE_FILE_SIZE); fseek(fp, 0, SEEK_SET); unsigned char *buf = malloc(EXPECTED_CPU_TRACE_FILE_SIZE); if (fread(buf, 1, EXPECTED_CPU_TRACE_FILE_SIZE, fp) != EXPECTED_CPU_TRACE_FILE_SIZE) { ASSERT(false); } fclose(fp); fp = NULL; SHA1(buf, EXPECTED_CPU_TRACE_FILE_SIZE, md); FREE(buf); sha1_to_str(md, mdstr0); ASSERT(strcmp(mdstr0, EXPECTED_CPU_TRACE_SHA) == 0); } while(0); unlink(output); FREE(output); PASS(); }
static void *cpu_thread(void *dummyptr) { #ifndef NDEBUG // Spamsung Galaxy Y running Gingerbread triggers this, wTf?! ASSERT_ON_CPU_THREAD(); #endif LOG("cpu_thread : initialized..."); struct timespec deltat = { 0 }; struct timespec disk_motor_time = { 0 }; struct timespec t0 = { 0 }; // the target timer struct timespec ti = { 0 }; // actual before time sample struct timespec tj = { 0 }; // actual after time sample bool negative = false; long drift_adj_nsecs = 0; // generic drift adjustment between target and actual int debugging_cycles = 0; unsigned long dbg_ticks = 0; #if DEBUG_TIMING int speaker_neg_feedback = 0; int speaker_pos_feedback = 0; unsigned long dbg_cycles_executed = 0; #endif audio_init(); speaker_init(); MB_Initialize(); run_args.emul_reinitialize = 1; cpu_runloop: do { LOG("CPUTHREAD %lu LOCKING FOR MAYBE INITIALIZING AUDIO ...", (unsigned long)cpu_thread_id); pthread_mutex_lock(&interface_mutex); if (emul_reinitialize_audio) { emul_reinitialize_audio = false; speaker_destroy(); extern void MB_SoftDestroy(void); MB_SoftDestroy(); audio_shutdown(); audio_init(); speaker_init(); extern void MB_SoftInitialize(void); MB_SoftInitialize(); } pthread_mutex_unlock(&interface_mutex); LOG("UNLOCKING FOR MAYBE INITIALIZING AUDIO ..."); if (run_args.emul_reinitialize) { reinitialize(); } LOG("cpu_thread : begin main loop ..."); clock_gettime(CLOCK_MONOTONIC, &t0); do { ////SCOPE_TRACE_CPU("CPU mainloop"); // -LOCK----------------------------------------------------------------------------------------- SAMPLE ti if (UNLIKELY(emul_pause_audio)) { emul_pause_audio = false; audio_pause(); } pthread_mutex_lock(&interface_mutex); if (UNLIKELY(emul_resume_audio)) { emul_resume_audio = false; audio_resume(); } if (UNLIKELY(emul_video_dirty)) { emul_video_dirty = false; video_setDirty(A2_DIRTY_FLAG); } clock_gettime(CLOCK_MONOTONIC, &ti); deltat = timespec_diff(t0, ti, &negative); if (deltat.tv_sec) { if (!is_fullspeed) { TIMING_LOG("NOTE : serious divergence from target time ..."); } t0 = ti; deltat = timespec_diff(t0, ti, &negative); } t0 = timespec_add(t0, EXECUTION_PERIOD_NSECS); // expected interval drift_adj_nsecs = negative ? ~deltat.tv_nsec : deltat.tv_nsec; // set up increment & decrement counters run_args.cpu65_cycles_to_execute = (cycles_persec_target / 1000); // cycles_persec_target * EXECUTION_PERIOD_NSECS / NANOSECONDS_PER_SECOND if (!is_fullspeed) { run_args.cpu65_cycles_to_execute += cycles_speaker_feedback; } if (run_args.cpu65_cycles_to_execute < 0) { run_args.cpu65_cycles_to_execute = 0; } MB_StartOfCpuExecute(); if (is_debugging) { debugging_cycles = run_args.cpu65_cycles_to_execute; } do { if (is_debugging) { run_args.cpu65_cycles_to_execute = 1; } run_args.cpu65_cycle_count = 0; cycles_checkpoint_count = 0; cpu65_run(&run_args); // run emulation for cpu65_cycles_to_execute cycles ... #if DEBUG_TIMING dbg_cycles_executed += run_args.cpu65_cycle_count; #endif if (is_debugging) { debugging_cycles -= run_args.cpu65_cycle_count; timing_checkpointCycles(); if (c_debugger_should_break() || (debugging_cycles <= 0)) { int err = 0; if ((err = pthread_cond_signal(&dbg_thread_cond))) { LOG("pthread_cond_signal : %d", err); } if ((err = pthread_cond_wait(&cpu_thread_cond, &interface_mutex))) { LOG("pthread_cond_wait : %d", err); } if (debugging_cycles <= 0) { break; } } if (run_args.emul_reinitialize) { pthread_mutex_unlock(&interface_mutex); goto cpu_runloop; } } } while (is_debugging); MB_UpdateCycles(); // TODO : modularize MB and other peripheral card cycles/interrupts ... speaker_flush(); // play audio TRACE_CPU_BEGIN("advance scanner"); video_scannerUpdate(); TRACE_CPU_END(); clock_gettime(CLOCK_MONOTONIC, &tj); pthread_mutex_unlock(&interface_mutex); // -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj if (timing_shouldAutoAdjustSpeed() && !is_fullspeed) { disk_motor_time = timespec_diff(disk6.motor_time, tj, &negative); if (UNLIKELY(negative)) { LOG("WHOA... time went backwards #1! Did you just cross a timezone?"); disk_motor_time.tv_sec = 1; } if (!speaker_isActive() && !video_isDirty(A2_DIRTY_FLAG) && (disk6.disk[disk6.drive].file_name != NULL) && !disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS) ) { TIMING_LOG("auto switching to full speed"); _timing_initialize(CPU_SCALE_FASTEST); } } if (!is_fullspeed) { deltat = timespec_diff(ti, tj, &negative); if (UNLIKELY(negative)) { LOG("WHOA... time went backwards #2! Did you just cross a timezone?"); deltat.tv_sec = 1; } long sleepfor = 0; if (LIKELY(!deltat.tv_sec)) { sleepfor = EXECUTION_PERIOD_NSECS - drift_adj_nsecs - deltat.tv_nsec; } if (sleepfor <= 0) { // lagging ... static time_t throttle_warning = 0; if (t0.tv_sec - throttle_warning > 0) { TIMING_LOG("not sleeping to catch up ... %ld . %ld", deltat.tv_sec, deltat.tv_nsec); throttle_warning = t0.tv_sec; } } else { deltat.tv_sec = 0; deltat.tv_nsec = sleepfor; ////TRACE_CPU_BEGIN("sleep"); nanosleep(&deltat, NULL); ////TRACE_CPU_END(); } #if DEBUG_TIMING // collect timing statistics if (speaker_neg_feedback > cycles_speaker_feedback) { speaker_neg_feedback = cycles_speaker_feedback; } if (speaker_pos_feedback < cycles_speaker_feedback) { speaker_pos_feedback = cycles_speaker_feedback; } if ((dbg_ticks % NANOSECONDS_PER_SECOND) == 0) { TIMING_LOG("tick:(%ld.%ld) real:(%ld.%ld) cycles exe: %d ... speaker feedback: %d/%d", t0.tv_sec, t0.tv_nsec, ti.tv_sec, ti.tv_nsec, dbg_cycles_executed, speaker_neg_feedback, speaker_pos_feedback); dbg_cycles_executed = 0; speaker_neg_feedback = 0; speaker_pos_feedback = 0; } #endif if ((dbg_ticks % NANOSECONDS_PER_SECOND) == 0) { dbg_ticks = 0; } } if (timing_shouldAutoAdjustSpeed() && is_fullspeed) { disk_motor_time = timespec_diff(disk6.motor_time, tj, &negative); if (UNLIKELY(negative)) { LOG("WHOA... time went backwards #3! Did you just cross a timezone?"); disk_motor_time.tv_sec = 1; } if (speaker_isActive() || video_isDirty(A2_DIRTY_FLAG) || (disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS)) ) { double speed = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor; if (speed <= CPU_SCALE_FASTEST_PIVOT) { TIMING_LOG("auto switching to configured speed"); _timing_initialize(speed); } } } if (UNLIKELY(run_args.emul_reinitialize)) { break; } if (UNLIKELY(emul_reinitialize_audio)) { break; } if (UNLIKELY(cpu_shutting_down)) { break; } } while (1); if (UNLIKELY(cpu_shutting_down)) { break; } } while (1); speaker_destroy(); MB_Destroy(); audio_shutdown(); cpu_thread_id = 0; cpu_pause(); disk6_eject(0); disk6_eject(1); return NULL; }
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEjectDisk(JNIEnv *env, jobject obj, jboolean driveA) { LOG("..."); disk6_eject(!driveA); }