夜、市川の江戸川沿いで愛犬の柴犬であるコテツを散歩させていると、夜空に見上げる月がやけに白く、そして冷ややかに輝いて見えることがある。最近、インターネット上のトレンドワードに「ポルノグラフィティ」の名前と、彼らの鮮烈なデビュー曲である「アポロ」が急浮上していた。そこから連鎖するように、半世紀以上も前の月着陸船アポロ11号の話題が改めて人々の間で飛び交っている。しかし、C言語やアセンブラを駆使して数々の泥臭い制御システムと対峙してきた52歳の組み込みエンジニアの視線は、懐メロの感傷などには向かわない。私の意識は、現代のスマートキーや家電のリモコンすら下回るわずかなメモリスペースで月面着陸という偉業を成し遂げた、アポロ誘導コンピュータ(AGC)の極限のハードウェア設計と、それを支えた職人たちの執念へと直行するのだ。現在の贅沢な開発環境に慣れきった若いエンジニアたちにこそ、この極限状態における設計哲学を知ってほしい。
ポルノグラフィティ「アポロ」から紐解く、月着陸船AGCの極限組み込み技術と手織りメモリの奇跡

AGCのメモリが数KBってマジ?今のスマートキーや家電のリモコン以下じゃん。

銅線を職人が手で編んで作ったメモリって、もはや工芸品。信じられない執念だな。
ポルノグラフィティのデビュー曲「アポロ」がテレビやラジオ、ネット上の各種メディアで流れるたびに、多くの人々は1969年の人類初の偉業を思い出す。しかし、技術者にとってのアポロ計画は、ロマンあふれる宇宙開発の歴史であると同時に、当時の最先端技術を極限まで絞り出した「組み込みソフトウェア開発の総力戦」の歴史である。今やスマートフォンのメモリがギガバイト単位で語られ、不要なオブジェクトがガーベジコレクションによって自動で片付けられる時代だが、アポロ11号の月着陸船に搭載されたアポロ誘導コンピュータ(Apollo Guidance Computer、通称AGC)のスペックを知れば、現代のエンジニアは例外なく絶句するだろう。
AGCのスペックは、現代の私たちが日常的に手にするどのデバイスよりも貧弱だ。RAMにあたる「Erasable Memory(消去可能メモリ)」はわずか2048ワード(約4KB)、ROMにあたる「Fixed Memory(固定メモリ)」も36,864ワード(約72KB)に過ぎない。クロック周波数は約2MHz。この限られたハードウェア資源のなかで、月着陸船の姿勢制御、軌道計算、ロケットエンジンの噴射制御、さらには宇宙飛行士との対話インターフェースである「DSKY」の処理をすべて同時に実行していたのだ。この信じがたい超極限状態のシステムがどのように設計され、いかなる泥臭い工夫によって動いていたのか、その裏側にある技術の本質を技術的視点から解き明かす。
ここが面白い:技術的背景とコミュニティの熱量
AGCの信頼性を語るうえで、絶対に外せないのがROMの物理的な構造である「コアロープメモリ(Core Rope Memory)」だ。当時の半導体技術は極めて未成熟であり、宇宙空間の過酷な放射線や振動に耐えうる高密度のROMを半導体で製造することは不可能であった。そこで採用されたのが、磁性体コア(フェライトコア)に一本一本銅線を通してデータを記録する、文字通りの「手編み」メモリである。銅線をコアに通すと「1」、コアの外側を通すと「0」として認識される物理構造を持っており、プログラムコードがそのまま物理的な配線パターンとして固定される仕組みだった。
この製造工程は、マサチューセッツ工科大学(MIT)の指導のもと、主に繊維工場の女性労働者たちによって手作業で行われた。彼女たちの緻密な作業から、この製造方法は「LOL(Little Old Ladies)メソッド」とも呼ばれている。もし一本でも銅線を通す位置を間違えれば、軌道計算プログラムのビットが反転し、月着陸船は宇宙の藻屑と化していた。そのため、顕微鏡を覗き込みながら針と糸を通すように銅線を編み込んでいく作業には、妥協のない職人技が求められた。書き換えが一切不可能なこの「物理的なROM」は、一度織り上がれば放射線の影響を全く受けず、不揮発性で究極の耐障害性を持つ。しかし、バグの修正が物理的に不可能であることを意味しており、開発チームに課せられたプレッシャーは想像を絶するものだった。
さらに興味深いのが、この極限のメモリ制限の中で動作していたソフトウェアのタスクスケジューリング機構だ。AGCのオペレーティングシステムは、現代の組み込みOSの先駆けともいえる「優先度付き協調的マルチタスク」を採用していた。このOSは、主に「Executive(エグゼクティブ)」と「Waitlist(ウェイトリスト)」という2つのキューを制御することで動作していた。Executiveは優先度に基づいて最大8つのタスクを順次実行し、Waitlistは時間管理された短時間の処理を未来の指定したタイミングで実行するための仕組みだ。タスクは自発的に処理を明け渡す必要がある協調的マルチタスクだったため、各タスクの優先度設定と実行時間の管理が生命線であった。
このAGCの優先度スケジューリングの論理構造を理解しやすくするために、簡略化したC++の動作シミュレーションコードを以下に提示する。現代のC++標準ライブラリを用いて、Executive of 優先度キューとWaitlistの動作、そして過負荷時の復旧(パージ)ロジックを再現したものだ。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
// AGCのタスクを表現する構造体
struct Task {
int id;
std::string name;
int priority; // 値が大きいほど高優先度
void (*work_func)(); // 実行する処理の関数ポインタ
};
// タイムアウト後に実行されるWaitlist用のタスク構造体
struct WaitlistTask {
int delay_ticks; // 実行までの遅延時間(簡易的なチック数)
Task task;
};
class AGCScheduler {
private:
static const int MAX_EXEC_TASKS = 8; // Executiveが同時に保持できる最大タスク数
std::vector<Task> executive_queue; // 優先度順にソートされるタスクリスト
std::vector<WaitlistTask> waitlist; // 時間管理タスクのリスト
public:
// Executiveにタスクを追加する
bool schedule_executive(const Task& new_task) {
if (executive_queue.size() >= MAX_EXEC_TASKS) {
// バッファオーバーフロー(1201/1202アラートのトリガー条件)
std::cerr << "[WARNING] Executive buffer overflow! Task dropped: "
<< new_task.name << " (Priority: " << new_task.priority << ")\n";
return false;
}
executive_queue.push_back(new_task);
// 優先度が高い順(降順)にソート
std::sort(executive_queue.begin(), executive_queue.end(),
[](const Task& a, const Task& b) {
return a.priority > b.priority;
});
return true;
}
// Waitlistに遅延タスクを追加する
void schedule_waitlist(int delay, const Task& task) {
waitlist.push_back({delay, task});
}
// システムクロック(チック)の更新。Waitlistの遅延を減算し、0になったらExecutiveに投入
void update_clock() {
auto it = waitlist.begin();
while (it != waitlist.end()) {
it->delay_ticks--;
if (it->delay_ticks <= 0) {
// 遅延時間が経過したため、Executiveへスケジュール
std::cout << "[Waitlist] Dispatching task to Executive: " << it->task.name << "\n";
if (!schedule_executive(it->task)) {
std::cout << "[ERROR] 1202 Alert! High priority tasks will override lower ones.\n";
// 1202アラート発生時、低優先度タスクを強制削除して空きを作る設計思想
handle_overload_recovery();
schedule_executive(it->task); // 再試行
}
it = waitlist.erase(it);
} else {
++it;
}
}
}
// 1202/1201アラート発生時の復旧処理(低優先度タスクのパージ)
void handle_overload_recovery() {
std::cout << "[Recovery] Purging low-priority tasks from Executive queue...\n";
// 優先度が低い下位タスクを削除する(例として、優先度5未満のタスクをパージ)
executive_queue.erase(
std::remove_if(executive_queue.begin(), executive_queue.end(),
[](const Task& t) { return t.priority < 5; }),
executive_queue.end()
);
}
// Executiveに積まれたタスクを1つ実行する(協調的マルチタスクのため、1周で1タスク処理)
void run_next_task() {
if (executive_queue.empty()) {
std::cout << "[Executive] Idle. No tasks to execute.\n";
return;
}
// 最も優先度の高いタスクを取り出して実行
Task current_task = executive_queue.front();
executive_queue.erase(executive_queue.begin());
std::cout << "[Executive] Running: " << current_task.name
<< " (Priority: " << current_task.priority << ")\n";
current_task.work_func();
}
bool has_tasks() const {
return !executive_queue.empty() || !waitlist.empty();
}
};
// テスト用のダミー処理関数群
void thrust_control() { std::cout << " -> Thruster control executed. Orbit stable.\n"; }
void radar_check() { std::cout << " -> Radar data processed. Distance measured.\n"; }
void update_display() { std::cout << " -> DSKY display refreshed (Low priority).\n"; }
int main() {
AGCScheduler scheduler;
// 初期タスクの投入
scheduler.schedule_executive({1, "Thruster Control", 10, thrust_control});
scheduler.schedule_executive({2, "Update Display", 2, update_display});
// Waitlistにレーダー処理を3チック後に実行するよう登録
scheduler.schedule_waitlist(3, {3, "Radar Process", 8, radar_check});
std::cout << "--- Cycle 1 ---\n";
scheduler.run_next_task(); // Thruster Controlが実行されるはず
std::cout << "\n--- Cycle 2 (Waitlist count down) ---\n";
scheduler.update_clock();
scheduler.run_next_task(); // Update Displayが実行される
std::cout << "\n--- Cycle 3 (Waitlist triggers) ---\n";
// 3チック経過させてWaitlistをトリガーさせる
scheduler.update_clock();
scheduler.update_clock();
scheduler.run_next_task(); // Radar Processが実行される
return 0;
}
上記のコードが示すように、Executiveは最大8つという厳格なバッファ制限(MAX_EXEC_TASKS)を持っている。このバッファが埋まることは、システムにとって致命的なリソースオーバーフローを意味する。このコードの肝は、バッファが溢れた際に呼び出される「handle_overload_recovery」関数だ。ここでは優先度の低いタスク(このシミュレーションでは優先度5未満のタスク)を容赦なくキューから排除(パージ)し、航法や姿勢制御といった最優先タスク用の空きスペースを強制的に作り出している。アポロ開発チームを率いたマーガレット・ハミルトンらは、この優先度に基づく非同期処理と自動復旧の仕組みをアセンブリ言語で構築していたのだ。
この設計の真価が問われたのが、アポロ11号が月面に着陸するわずか数分前のことだった。着陸船の高度が下がる中、宇宙飛行士の操作ミスあるいは手順書の不備により、本来は着陸時に不要であるはずの「ランデブーレーダー」のスイッチが入ったままになってしまった。このレーダーは毎秒何千回ものデータ更新割り込みをAGCに送りつけ、CPUの使用率は一瞬で設計限界を超えた。これによって発生したのが、世界的に有名な「1202アラート(および1201アラート)」である。これはExecutiveのタスクリストがパンクしたことを示すリソースオーバーフローのエラーコードだった。
もしこの時、AGCが現代の無防備なOSのようにハングアップしたり、ブルー・スクリーンを表示して強制再起動していたら、アポロ11号は月面に衝突していた。しかし、ハミルトンたちが設計したソフトウェアは、1202アラートを排出しながらも、優先度の低い「DSKYへの画面表示処理」を自ら切り捨て、宇宙飛行士の操作用バッファを開放した。着陸船 of エンジニアは、最優先タスクである着陸船のエンジン噴射と高度制御だけを毎ミリ秒愚直に実行し続けた。コンピュータは警告を発しながらも仕事を継続し、アームストロング船長は手動操縦を交えて無事に静かの海に着陸した。ハードウェアが過負荷に陥ることをあらかじめ想定し、ソフトウェア側で致命的なクラッシュを防ぐ「回復力(レジリエンス)」を組み込んでおくという設計思想の勝利だった。
この1202アラートと過負荷によるシステム崩壊の危機は、私にとっても決して遠い宇宙の出来事ではない。1990年代の後半、私が20代の若手エンジニアだった頃、千葉県市川市にある大型物流センターの自動搬送コンベアシステムの立ち上げで似たような大惨事を経験した。当時使用していたのは安価な8ビットのワンチップマイコンで、プログラムはアセンブリとC言語で書かれていた。搬送される荷物を検出する複数の光電センサーからの信号を、ハードウェア外部割り込み(IRQ)で直接受け取り、荷物の位置をトラッキングしながら、仕分け用の巨大なエアシリンダープッシャー(アーム)を駆動させるタイミングを計算するシステムだった。
年末の繁忙期、ラインの搬送速度を急遽引き上げたところ、一台のセンサーの取り付け金具が現場の激しい振動で緩み、高速でチャタリング(ON/OFF信号 of 数キロヘルツでの異常連打)を起こし始めた。CPUはセンサーからの狂ったような割り込み処理(ISR)に全てのクロックサイクルを奪われ、メインループで行っていたアームの駆動遅延計算処理が完全にスタベーション(飢餓状態)に陥った。割り込み優先度の設計が甘く、センサーの入力処理がモーター制御タスクよりも優先されていたためだ。アームを引っ込める処理のタスクが実行されないまま、次の200キログラムの木製パレットが時速20キロメートルで突っ込んできた。強烈な衝撃音とともにスチール製のアームはねじ曲がり、コンベアのフレームはひしゃげ、火花が散ってライン全体が緊急停止した。
私は冷え切った冬の倉庫の床に悲痛な思いで這いつくばり、微かに響くエラーブザーの音を聞きながら、胃を雑巾のように絞られる思いでオシロスコープとロジックアナライザを睨み続けた。48時間一睡もせず、缶コーヒーの空き缶を山のように積み上げながら、割り込みハンドラ内に時間あたりのトリガー上限を設けるガードコードを追加し、タスク優先度キューを再実装した。もし当時の私がアポロ計画のExecutiveのパージ設計思想を深く理解し、過負荷時に不要なセンサースタックを捨てて駆動停止処理を最優先する堅牢さをコードに持たせていれば、機械を物理的に破壊するような惨事は防げただろう。あの時の冷たい金属の擦れる音と、自分のコードが招いた破壊の光景は、今でも私のエンジニアとしての背筋を寒くさせる。
この話題をどう見るか?:現実的な視点と利用価値
現代のソフトウェア開発シーンを見渡すと、リソースの制約から解放されたかのような錯覚に陥ることが多い。Webブラウザ上で動作するチャットツールひとつが数百メガバイトのメモリを平気で消費し、ちょっとしたWeb APIサーバーを立ち上げるだけで数ギガバイトのコンテナイメージが必要になる。ハードウェアの進化がソフトウェアの肥大化を許容してきたため、メモリのバイト数を節約することや、CPUの命令サイクル数を削る努力は「早期の最適化は諸悪の根源」という大義名分の影に隠れて、顧みられなくなっている。
しかし、本当にリソースは無限なのだろうか。例えば、自動車のECU(電子制御ユニット)や、家庭に普及しているスマートロックなどのIoTエッジデバイス、あるいは医療用のペースメーカーといった「失敗が許されない制御システム」においては、今でも数キロバイト、数メガバイトのレベルでの厳格なリソース管理が日常的に行われている。もしスマートロックの通信ライブラリがメモリリークを起こして突然フリーズしたり、車の自動ブレーキ制御が不要なログ出力処理にCPUサイクルを奪われて動作が数ミリ秒遅延すれば、それはユーザーの利便性を損なうだけでなく、人命に関わる重大な事故に直結する。
アポロ計画のAGCが私たちに教えてくれるのは、システムが過負荷に陥った際の「優雅な機能縮退(Graceful Degradation)」の設計手法だ。これは現代のクラウドアーキテクチャにおける「レートリミッター」や「サーキットブレイカー」、Webサーバーの「バックプレッシャー(背圧)」制御と本質的に全く同じ概念である。アクセスが集中してサーバーがダウンしそうな時に、エラーページを素早く返してコアな決済処理のデータベース接続を守る設計は、AGCが1202アラートを出しながらDSKYの表示を諦めて月着陸制御を守った姿そのものだ。私たちは数ギガバイトのクラウド環境であっても、この「物理的な限界を迎えたときにどう振る舞うべきか」という泥弱な制御哲学を忘れてはならない。
導入・試す前の実用メモ
- 【確認点】タスク優先度の静的設計:リアルタイムOS(RTOS)を使用するプロジェクトでは、初期段階で全タスクの実行周期と実行時間、および優先度のマッピングテーブルを作成し、最悪時間内でのデッドラインミスが発生しないかを静的解析で確認すること。
- 【落とし穴】優先度の逆転(Priority Inversion):低優先度タスクがロック(セマフォ)を取得している間に、中優先度タスクが割り込み、結果として高優先度タスクがいつまでも処理を開始できなくなる罠がある。RTOSを使用する際は、必ず優先度継承(Priority Inheritance)プロトコルが有効になっているかを確認する必要がある。
- 【選択のヒント】協調的マルチタスクとプリエンプティブマルチタスク:極めてタイトなメモリ制約があり、かつタスクの動作時間が厳密に予測可能なシステムでは、コンテキストスイッチのオーバーヘッドが少ない協調的マルチタスク(AGC方式)をあえて採用する価値がある。一方、タスクの実行時間が動的に変化する場合は、OSが強制的にCPU権を奪うプリエンプティブ方式を選ぶのが無難だ。
まとめ:運営者としての現場判断
ポルノグラフィティの「アポロ」がトレンド入りしたことをきっかけに、半世紀前の組み込み技術に再び光が当たったことは、一人の古いエンジニアとして非常に喜ばしい。しかし、単なる歴史的な職人技の美談として終わらせてはならない。AGCの設計が現代の私たちに突きつけているのは、「動けばいいコード」と「サバイブできるシステム」の決定的な違いである。AIが数秒で何百行ものコードを出力する現代において、コードの記述量は飛躍的に増えたが、そのコードが「どのように物理的な限界に立ち向かうか」を深く理解しているエンジニアはどれほどいるだろうか。
私たちの現場における判断基準は常に明確であるべきだ。どんなにリソースが豊かな開発環境であっても、例外処理やリソース枯渇時の動作シナリオを人任せ(ライブラリ任せ)にしてはならない。システムが極限状態に達した時、どの処理を捨ててどの処理を死守すべきかという「優先順位」を明示的にコードに刻み込むこと。それこそが、手作業で銅線を編み込み、月へ人間を送り届けた先人たちの遺産を受け継ぐエンジニアの責任だ。
今夜も市川の静かな夜道でコテツを歩かせながら、白く輝く月を見上げる。半世紀以上前にあの場所に降り立った着陸船の内部で、4KBという極小のRAMが狂ったような割り込みを跳ね除けながら必死に計算を続けていたのだ。その熱い歴史に思いを馳せると、明日からの開発現場におけるコードの書き方に、一層の厳しい規律を宿らせるのだ。
広告・アフィリエイトリンクを含みます。商品選定は記事内容との関連性を優先しています。


