# How the EXECUTIVE System Manages Jobs and Tasks in the Apollo Guidance Computer

> Learn how the EXECUTIVE system manages jobs and tasks in the Apollo Guidance Computer with its preemptive priority scheduler, core job queues, and VAC memory allocation.

- Repository: [Chris Garry/Apollo-11](https://github.com/chrislgarry/Apollo-11)
- Tags: internals
- Published: 2026-03-04

---

**The EXECUTIVE system implements a preemptive priority scheduler that maintains job queues in 12‑word core sets, allocates VAC memory areas for tasks requiring private address space, and dispatches execution via the `NOVAC`, `FINDVAC`, and `CHANJOB` routines triggered by hardware interrupts.**

The Apollo Guidance Computer (AGC) relied on a sophisticated multitasking kernel known as the EXECUTIVE to manage concurrent operations ranging from guidance calculations to I/O handling. Found in the `Comanche055/EXECUTIVE.agc` file of the chrislgarry/Apollo‑11 repository—the Command Module’s flight software—this system demonstrates how 1960s spaceflight code achieved deterministic multitasking within severe hardware constraints. Understanding how the EXECUTIVE system manages jobs and tasks reveals the architectural decisions that enabled safe lunar landing operations through priority‑driven scheduling and banked memory management.

## Core Data Structures: Core Sets and VAC Areas

The EXECUTIVE organizes jobs using **core sets**—fixed 12‑word blocks in erasable memory. The first word stores the **2‑CADR** (address of the job’s entry point), the second holds the **priority** (signed 9‑bit value), and the remaining words preserve registers required for resumption. When a job must yield the processor, its complete context is saved into this structure, allowing the `CHANJOB` routine (lines 207‑242) to restore it later, including the **Superbank** (`SUPERBNK`) register that tracks the current memory bank.

For tasks requiring dedicated address space, the EXECUTIVE provides **VAC (Virtual Addressable Core) areas**. When `FINDVAC` is invoked, the system scans the `VAC1USE` through `VAC5USE` registers to locate a free area, reserving the first available one before inserting the job into the queue. Jobs that do not need private memory use `NOVAC` instead, skipping the VAC allocation overhead.

## Job Entry and Memory Allocation

### Submitting Lightweight Jobs with NOVAC

The `NOVAC` routine (lines 33‑49) handles jobs that execute within the existing core set without requiring a VAC. The caller loads the accumulator with the job’s entry address and priority, then triggers an interrupt:

```assembly
        TS      NEWLOC          ; Store entry address
        TS      NEWPRIO         ; Store priority (e.g., 5)
        INHINT                  ; Trigger EXECUTIVE entry

```

The EXECUTIVE catches the `INHINT`, saves these values into the next available core set, and returns control to the interrupted program. The new job remains dormant until the priority scanner selects it.

### Allocating Private Memory with FINDVAC

When a job needs isolation, `FINDVAC` (lines 50‑61) executes a VAC‑allocation loop in `FINDVAC2`. It searches the VAC‑use registers and, upon finding a free area, reserves it before calling `FINDVAC2` to complete the insertion. This mechanism prevents memory collisions between interpretive jobs and basic jobs sharing the same bank space.

## Job Suspension and Context Switching

### Suspending Active Jobs via CHANG1 and CHANG2

The EXECUTIVE provides distinct suspension paths for different execution modes. **Basic jobs** use `CHANG1` (lines 72‑76), which saves the current core set and transfers control to `CHANJOB`. **Interpretive jobs**—which run using the AGC’s vector processing interpreter—use `CHANG2` (lines 78‑86), performing extra bookkeeping for the negative LOC flag that distinguishes interpretive state.

The `CHANJOB` routine performs the heavy lifting of context switching (lines 207‑242). It swaps all general registers, updates `SUPERBNK` to reflect the new job’s memory bank, and jumps to the restored program counter. This bank‑aware switching ensures that jobs residing in different fixed memory banks execute correctly despite the AGC’s 15‑bit address space limitations.

## Job Synchronization: Sleep and Wake Primitives

### Putting Jobs to Sleep with JOBSLEEP

Jobs awaiting external events (such as telemetry I/O) call `JOBSLEEP` (lines 90‑94). This routine marks the job as inactive by storing its priority in a **sleep‑priority** register, effectively making the priority negative. The job remains in its core set but is ignored by the priority scanner until reawakened.

```assembly
        TS      PRIORITY        ; Mark as sleeping (negative priority)
        INHINT                  ; EXECUTIVE runs JOBSLEEP

```

### Awakening Jobs via JOBWAKE

The `JOBWAKE` routine (lines 95‑104) scans all core sets for a job whose saved location matches `NEWLOC`. Upon finding a match, it restores the job’s positive priority value and re‑queues it for execution. This event‑driven model allows hardware interrupts to trigger job resumption without polling loops.

## Dynamic Priority Management and Termination

### Changing Priorities Mid‑Execution with PRIOCHNG

Real‑time constraints sometimes require elevating or demoting a running job’s urgency. The `PRIOCHNG` routine (lines 106‑114) accepts a new priority value in the accumulator, stores it in the current core set, and forces a rescan of the priority list. This can trigger immediate preemption if a higher‑priority job becomes runnable.

### Job Completion with ENDOFJOB

When a task finishes, it calls `ENDOFJOB` (lines 115‑119). This routine clears the job’s entry from the core set, restores the EXECUTIVE bank, and immediately invokes the priority scanner to dispatch the next eligible task. There is no “return to caller”; control flows directly to the highest‑priority ready job or enters the idle loop.

## The Priority Scheduling Algorithm

The heart of the EXECUTIVE is the **EJSCAN** routine (lines 85‑110), implemented through the `EJ1` and `EJ2` subroutines. This scanner iterates over core sets in 12‑word steps, examining the priority registers to locate the highest positive value (active job). Because priorities are signed 9‑bit numbers, negative values (sleeping jobs) are automatically skipped. Once identified, the dispatcher loads the corresponding 2‑CADR and initiates a context switch via `CHANJOB`.

This deterministic scan ensures that critical guidance tasks—typically assigned priorities in the range 16‑31—always preempt lower‑priority housekeeping jobs, maintaining the millisecond‑level timing guarantees required for navigation.

## System Idle and Interrupt Handling

When no jobs are pending, the EXECUTIVE enters an idle state through `ADVAN`, `DUMMYJOB`, and `SELFBANK` (lines 121‑133). This loop turns off the green activity light and repeatedly checks for new job requests via the interrupt system. Entry into the EXECUTIVE always occurs through the `INHINT` (interrupt inhibit) mechanism, ensuring atomic access to the job queue during insertion, deletion, or priority modification operations.

## Summary

- **Core sets** provide 12‑word containers for job state, storing 2‑CADR addresses, priorities, and resumption registers in contiguous erasable memory.
- **VAC allocation** via `FINDVAC` gives jobs private address space, while `NOVAC` offers lightweight entry for stateless tasks.
- **Context switching** relies on `CHANJOB` to swap registers and Superbank settings, enabling preemptive multitasking across the AGC’s banked memory architecture.
- **Synchronization primitives** `JOBSLEEP` and `JOBWAKE` support event‑driven programming without polling overhead.
- **Priority management** through `PRIOCHNG` allows dynamic urgency adjustment, while `ENDOFJOB` provides clean termination and immediate redispatch.
- **EJSCAN** implements a deterministic priority queue scan, ensuring highest‑urgency tasks execute first.

## Frequently Asked Questions

### What is the difference between NOVAC and FINDVAC in the AGC EXECUTIVE?

**NOVAC** (lines 33‑49) inserts a job that runs in the existing core set without private memory, making it suitable for simple, self‑contained tasks. **FINDVAC** (lines 50‑61) first allocates a **VAC area** from the limited pool of five available spaces, then inserts the job, providing isolated address space for complex interpretive routines that might otherwise collide with other processes.

### How does the EXECUTIVE system handle job preemption?

Preemption occurs when a higher‑priority job enters the queue or when `PRIOCHNG` elevates an existing job’s priority above the currently running task. The `EJSCAN` routine detects the priority inversion during its next execution cycle, and `CHANJOB` (lines 207‑242) performs the context switch by saving the current job’s registers—including the `SUPERBNK` value—and loading those of the higher‑priority job.

### What are VAC areas and why are they necessary?

**VAC (Virtual Addressable Core) areas** are dedicated memory regions assigned to jobs that require persistent local variables or interpretive execution context. Because the AGC uses a shared memory model with bank switching, VAC areas prevent data corruption between concurrent jobs. The EXECUTIVE maintains five VAC registers (`VAC1USE` through `VAC5USE`) and grants them on a first‑available basis during `FINDVAC` processing.

### How does the AGC EXECUTIVE enter the idle state?

When `EJSCAN` finds no active (positive priority) jobs, control flows to `ADVAN` and `DUMMYJOB` (lines 121‑133). This **idle loop** disables the activity indicator and halts meaningful execution until the next hardware interrupt arrives. The `SELFBANK` routine ensures the EXECUTIVE remains in its own memory bank while waiting, allowing immediate response to `INHINT` signals that indicate new job requests or wake events.