/** * Process a switch that has transitioned states. All debouncing * is fully completed prior to this call. * * The transition is first latched, so that polling the switch level * will return the new state. At the same time, IRQ-level scanning * is restarted, so that further transitions can be detected. * * Second, if the transition requires scheduling, a task is started * to handle it. Eligibility depends on whether or not the switch * is declared as an edge switch (meaning it is scheduled on both * types of transitions) and whether or not it is an opto (i.e. * do we schedule open-to-closed or closed-to-open?) * * The switch is also known not to be in the debounce queue prior to this * function being called. */ void switch_transitioned (const U8 sw) { /* Latch the transition. sw_logical is still an open/closed level. * By clearing the stable/unstable bits, IRQ will begin scanning * for new transitions at this point. */ disable_irq (); bit_toggle (sw_logical, sw); bit_off (sw_stable, sw); bit_off (sw_unstable, sw); enable_irq (); /* See if the transition requires scheduling. It does if the switch is declared 'edge' (it schedules when becoming active or inactive), otherwise only becoming active. Because most switches are not edge, check for the active state first. */ if (switch_poll_logical (sw) || bit_test (mach_edge_switches, sw)) { #ifdef CONFIG_BPT /* One extra condition : do not schedule any switches when the system is paused */ if (db_paused != 0) return; #endif /* Start a task to process the switch event. This task may sleep if necessary, but it should be as fast as possible and push long-lived operations into separate background tasks. It is possible for more than one instance of a task to exist for the same switch, if valid debounced transitions occur quickly. */ task_pid_t tp = task_create_gid (GID_SW_HANDLER, switch_sched_task); task_set_arg (tp, sw); } }
/* TODO - this function is identical to the 6809 version */ task_pid_t task_create_gid1 (task_gid_t gid, task_function_t fn) { task_pid_t tp = task_find_gid (gid); if (tp) return (tp); return task_create_gid (gid, fn); }
/** Starts the thread for the currently running display effect. */ static void deff_start_task (const deff_t *deff) { task_pid_t tp; deff_debug ("deff_start_task\n"); /* Stop whatever deff is running now */ task_kill_gid (GID_DEFF); deff_stop_task (); /* If this deff pauses kickouts, handle that now */ if (deff->flags & D_PAUSE) kickout_lock (KLOCK_DEFF); /* If this deff wants to show the last score, hold * on to that value */ if (deff->flags & D_SCORE) score_deff_set (); /* Create a task for the new deff */ tp = task_create_gid (GID_DEFF, deff->fn); if (tp) { if (deff->page != 0xFF) task_set_rom_page (tp, deff->page); task_set_duration (tp, TASK_DURATION_INF); } }
/* TODO - this function is identical to the 6809 version */ task_pid_t task_recreate_gid (task_gid_t gid, task_function_t fn) { task_kill_gid (gid); #ifdef PARANOID if (task_find_gid (gid)) fatal (ERR_TASK_KILL_FAILED); #endif return task_create_gid (gid, fn); }
static void status_report_check (void) { if (task_find_gid (GID_STATUS_REPORT_MONITOR)) { status_report_cancel_delay = TRUE; } else task_create_gid (GID_STATUS_REPORT_MONITOR, status_report_monitor); }
static void award_loop (void) { /* Start a timer to go down a level after 10 seconds */ if (!task_find_gid (GID_LOOP_LEVEL_TIMER)) task_create_gid (GID_LOOP_LEVEL_TIMER, loop_level_timer_task); bounded_increment (total_loops, 254); deff_start (DEFF_LOOP); }
CALLSET_ENTRY (rocket, dev_rocket_kick_attempt) { if (in_live_game) { if (!multi_ball_play ()) leff_start (LEFF_ROCKET); sound_send (SND_ROCKET_KICK_REVVING); task_sleep (TIME_500MS); task_create_gid (0, rocket_kick_sound); } }
void amode_flipper_sound (void) { if (!task_find_gid (GID_AMODE_FLIPPER_SOUND_DEBOUNCE)) { task_create_gid (GID_AMODE_FLIPPER_SOUND_DEBOUNCE, amode_flipper_sound_debounce_timer); #ifdef MACHINE_AMODE_FLIPPER_SOUND_CODE sound_send (MACHINE_AMODE_FLIPPER_SOUND_CODE); #endif } }
CALLSET_ENTRY (slot, dev_slot_kick_attempt) { if (in_live_game) { task_sleep (TIME_200MS); while (kickout_locks > 0) task_sleep (TIME_500MS); /* start Slot kick -> STDM timer for combo.c */ sound_send (SND_SLOT_KICKOUT_1); leff_start (LEFF_SLOT_KICKOUT); task_sleep (TIME_500MS); task_create_gid (0, slot_kick_sound); event_can_follow (slot_kick, outhole, TIME_1S); } }
CALLSET_ENTRY (slot, dev_slot_kick_attempt) { /* TODO Hack to hold ball due to the way shot_slot_task works */ while (deff_get_active () == DEFF_DOOR_AWARD) task_sleep (TIME_500MS); while (kickout_locks != 0) task_sleep (TIME_500MS); if (in_live_game) { /* start Slot kick -> STDM timer for combo.c */ event_can_follow (slot_kick, outhole, TIME_1S); sound_send (SND_SLOT_KICKOUT_1); leff_start (LEFF_SLOT_KICKOUT); task_sleep (TIME_500MS); task_create_gid (0, slot_kick_sound); } }
CALLSET_ENTRY (rocket, dev_rocket_kick_attempt) { if (in_live_game) { /* Wait until the skill shot has finished */ while (skill_shot_enabled || deff_get_active () == DEFF_SKILL_SHOT_MADE || task_find_gid (GID_SKILL_SWITCH_TRIGGER)) { task_sleep (TIME_500MS); } if (!multi_ball_play ()) leff_start (LEFF_ROCKET); sound_send (SND_ROCKET_KICK_REVVING); deff_start (DEFF_ROCKET); task_sleep (TIME_500MS); task_create_gid (0, rocket_kick_sound); } }
/** * Handle the push button that toggles the state of mute/pause if enabled. */ CALLSET_ENTRY (mute_and_pause, sw_buyin_button) { if (feature_config.mute_pause == NO) { return; } else if (!in_live_game) { return; } else if (task_find_gid (GID_MUTE_AND_PAUSE)) { /* Stop mute/pause mode */ mute_and_pause_stop (); } else { /* Start mute/pause mode */ task_create_gid (GID_MUTE_AND_PAUSE, mute_and_pause_monitor); } }
/** Initialize the FreeWPC program. */ __noreturn__ void freewpc_init (void) { extern __common__ void system_reset (void); /* Initialize the platform specifics first */ VOIDCALL (platform_init); /* Reset the blanking and watchdog circuitry. * Eventually, the watchdog will be tickled every 1ms * by the IRQ; until interrupts are enabled, we will * have to do this periodically ourselves. */ pinio_watchdog_reset (); /* Set init complete flag to false. When everything is * ready, we'll change this to a 1. */ sys_init_complete = 0; periodic_ok = 0; sys_init_pending_tasks = 0; /* Initialize all of the other kernel subsystems, * starting with the hardware-centric ones and moving on * to software features. */ /* Initialize the real-time scheduler. The periodic functions are scheduled at compile-time using the 'gensched' utility. */ VOIDCALL (tick_init); /* Initialize the hardware. * After each init call, tickle the watchdog (IRQ isn't enabled yet) * to prevent it from expiring and resetting the CPU. * We don't use a callset here because there are some ordering * dependencies -- some modules must be initialized before others -- * and gencallset doesn't allow us to express those conditions. */ #ifdef DEBUGGER db_init (); bpt_init (); pinio_watchdog_reset (); #endif ac_init (); pinio_watchdog_reset (); sol_init (); pinio_watchdog_reset (); #ifdef CONFIG_GI gi_init (); pinio_watchdog_reset (); #endif display_init (); pinio_watchdog_reset (); switch_init (); pinio_watchdog_reset (); flipper_init (); pinio_watchdog_reset (); lamp_init (); pinio_watchdog_reset (); device_init (); pinio_watchdog_reset (); free_timer_init (); pinio_watchdog_reset (); sound_init (); pinio_watchdog_reset (); #if (MACHINE_PIC == 1) pic_init (); pinio_watchdog_reset (); #endif /* task_init is somewhat special in that it transforms the system * from a single task into a multitasking one. After this, tasks * can be spawned if need be. A task is created for the current * thread of execution, too. */ task_init (); pinio_watchdog_reset (); #ifdef CONFIG_NATIVE /* Notify the simulator when the core OS is up and running. */ sim_init (); #endif /* Initialize the sound board early in a background * thread, since it involves polling for data back from it, * which may take unknown (or even infinite) time. */ sys_init_pending_tasks++; task_create_gid (GID_SOUND_INIT, sound_board_init); /* Enable interrupts (IRQs and FIRQs). Do this as soon as possible, * but not before all of the hardware modules are done. */ enable_interrupts (); /* Initialize everything else. Some of these are given explicitly to force a particular order, since callsets do not guarantee the order of invocation. For most things the order doesn't matter. */ deff_init (); leff_init (); test_init (); adj_init (); log_init (); callset_invoke (init); /* Check all adjustments and make sure that their checksums are valid. If problems are found, those adjustments will be made sane again. */ csum_area_check_all (); /* Enable periodic processing. */ periodic_ok = 1; task_sleep (TIME_16MS); /* The system is initialized from a hardware perspective. * Now, perform additional final initializations. */ system_reset (); /* The system can run itself now, this task is done! */ task_exit (); }
CALLSET_ENTRY (music, slow_down_music) { task_create_gid (GID_MUSIC_SPEED, slow_down_music_task); }
void tsm_mode_init (void) { task_create_gid (GID_JETS_ACTIVE_TASK, jets_active_task); score_zero (tsm_mode_total); init_all_dollars (); }
CALLSET_ENTRY (gumball, sw_far_left_trough) { if (!in_test) task_create_gid (GID_FAR_LEFT_TROUGH_MONITOR, sw_far_left_trough_monitor); }
/* * The entry point for processing a switch transition. It performs * some of the common switch handling logic before calling all * event handlers. Then it also performs some common post-processing. * This function runs in a separate task context for each switch that * needs to be processed. */ void switch_sched_task (void) { const U8 sw = (U8)task_get_arg (); const switch_info_t * const swinfo = switch_lookup (sw); /* Ignore any switch that doesn't have a processing function. This shouldn't ever happen if things are working correctly, but it was observed on PIC games when the PIC code is broken and reporting bad switch returns. genmachine ensures that all defined switches have a non-null value (null_function if nothing needs to be done) */ if (swinfo->fn == 0) goto cleanup; /* For test mode : this lets it see what was the last switch * to be scheduled. Used by the Switch Edges test. */ sw_last_scheduled = sw; log_event (SEV_INFO, MOD_SWITCH, EV_SW_SCHEDULE, sw); /* Don't service switches marked SW_IN_GAME if we're * not presently in a game */ if ((swinfo->flags & SW_IN_GAME) && !in_game) goto cleanup; /* Don't service switches not marked SW_IN_TEST, unless we're * actually in test mode */ if (!(swinfo->flags & SW_IN_TEST) && in_test) goto cleanup; /* If the switch has an associated lamp, then flicker the lamp when * the switch triggers. */ if ((swinfo->lamp != 0) && in_live_game) { task_pid_t tp = task_create_gid (GID_SWITCH_LAMP_PULSE, switch_lamp_pulse); lamp_pulse_data_t *cdata = task_init_class_data (tp, lamp_pulse_data_t); cdata->swinfo = swinfo; } /* If we're in a live game and the switch declares a standard * sound, then make it happen. */ if ((swinfo->sound != 0) && in_live_game) sound_send (swinfo->sound); #ifdef DEBUGGER if (swinfo->fn != null_function && sw < 72) { dbprintf ("SW: "); sprintf_far_string (names_of_switches + sw); dbprintf1 (); #ifdef DEBUG_SWITCH_NUMBER dbprintf (" (%d) ", sw); #endif dbprintf ("\n"); } #endif /* If the switch declares a processing function, call it. */ if (swinfo->fn) callset_pointer_invoke (swinfo->fn); /* Declare this as a hardware event that affects the random number generator. */ random_hw_event (); /* If a switch is marked SW_PLAYFIELD and we're in a game, * then call the global playfield switch handler and check for * valid playfield. Also, reset the ball search timer so that * it doesn't expire. */ if ((swinfo->flags & SW_PLAYFIELD) && in_game) { callset_invoke (any_pf_switch); /* If valid playfield was not asserted yet, then see if this * switch validates it. Most playfield switches do this right * away, but for some switches, like special solenoids (jets * or slings), which could repeatedly trigger if misaligned, * count the activations and validate only when some number * of different switches have triggered. Device counting * switches are ignored here, but an 'enter' event will * set it. */ if (!valid_playfield) { if (swinfo->flags & SW_NOVALID) { if (!SW_HAS_DEVICE (swinfo)) try_validate_playfield (sw); } else set_valid_playfield (); } ball_search_timer_reset (); } cleanup: /* If the switch is part of a device, then let the device * subsystem process the event. Note this will always occur * regardless of any of the above conditions checked. */ if (SW_HAS_DEVICE (swinfo)) device_sw_handler (SW_GET_DEVICE (swinfo)); task_exit (); }
CALLSET_ENTRY (tz_amode, amode_start) { task_create_gid (GID_LOCK_AND_OUTHOLE_MONITOR, lock_and_outhole_monitor); }
void burnin_init (void) { time_audit_clear (&burnin_duration); task_create_gid (GID_WINDOW_THREAD, burnin_thread); flipper_enable (); }
CALLSET_ENTRY (music, speed_up_music) { task_create_gid (GID_MUSIC_SPEED, speed_up_music_task); }
/** * This task runs during a switch stress test. During a game, * it randomly invokes switch handlers as if the switches had actually * been activated by the pinball. */ void switch_stress_task (void) { U8 sw; const switch_info_t *swinfo; task_pid_t tp; /* Delay a few seconds before starting the simulation. This allows time for the Start Button to be used to add players, instead of simulating endball. */ task_sleep_sec (3); task_create_peer (switch_stress_flipper_task); for (;;) { task_sleep (TIME_100MS); if (in_test) continue; /* Choose a switch at random. Skip certain well-known switches that are * never to be activated. */ sw = random_scaled (NUM_SWITCHES); #ifdef SW_ALWAYS_CLOSED if (sw == SW_ALWAYS_CLOSED) continue; #endif #ifdef MACHINE_OUTHOLE_SWITCH if (sw == MACHINE_OUTHOLE_SWITCH) continue; #endif /* Lookup the switch properties. Skip switches which aren't normally * activated on the playfield. For switches in a ball container, * simulate device entry there, otherwise simulate a switch event. */ swinfo = switch_lookup (sw); if (SW_HAS_DEVICE (swinfo)) { device_t *dev = device_entry (SW_GET_DEVICE (swinfo)); if (trough_dev_p (dev)) { /* Don't always trigger the trough device. The probability of a ball drain is treated as proportional to the number of balls in play. So in big multiballs, we allow this to happen more frequently. We need to do this occasionally so that multiball modes will eventually end, else nothing else gets tested. */ if (random () < CONFIG_STRESS_DRAIN_PROB * live_balls) switch_stress_drain (); } else if (dev->max_count < dev->size) { /* Don't throw an enter event if the device thinks it is full: it has "locked" as many balls as it can hold. The device code will throw a fatal if it sees this, which should never happen with real balls. */ dbprintf ("Sim. enter dev %d\n", dev->devno); device_call_op (dev, enter); task_sleep (TIME_1S); } } else if (swinfo->flags & SW_PLAYFIELD) { /* Simulate the switch */ tp = task_create_gid (GID_SW_HANDLER, switch_sched_task); task_set_arg (tp, sw); } } }
task_pid_t timer_start (task_gid_t gid, U16 ticks, task_function_t fn) { task_pid_t tp = task_create_gid (gid, fn); task_set_arg (tp, ticks); return (tp); }