エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

Pascalの試練場

"Wizardry: Proving Grounds of the Mad Overlord" *1 のコードを GitHub に見つけてしまいました。

github.com

Apple IIPascal で書かれていたという話は昔から聞いていて、ときおり思い出したように検索したりしていたのですが、とうとうヒットしてしまいました。

…とはいえ。 これが本当に当時のコードなのか確証はなく。

ただ少なくともコードの書き方から、当時使われていた UCSD Pascal のコードであることは間違いないようです。

また main ブランチの最新の状態は、デバッグされ多少改変されています。 GitHub のコミットを追いかけてみると、コミットメッセージが "original files" となっているこのコミットで最初のコードが追加されたようです。

GitHub - snafaru/Wizardry.Code at 67b86417e6824b1f1cddc4790a15cae704d6d799

読んでみると、当時のプログラミングが垣間見えて、興味深いものがあります。

TILTOWAIT!

例えば、あの有名な "TILTOWAIT" ですが、実はコード中に表記の揺れが認められます。

        TILTOWAI = 11157;
                7:  SPELLCAS := TILTOWAI;
          IF SPELL = TILTOWAIT THEN

それぞれの名前の定数が定義されているのかとも思いましたが、そのような定義も見当たりません。

ふと思い出したのが、識別子の名前として認識される最大長の存在。

以前購入した Borland C++ のマニュアルに識別子として認識される最大長が決まっていて、その長さを超えた分は区別されないという仕様がありました。

ネットで UCSD Pascal のマニュアルを探して識別子の項目を読んでみると。

Identifiers are character strings starting with an alpha character. Other characters must be alphanumeric or the ASCII underline ('_'). Only the first 8 characters are meaningful to the assembler even though more may be entered.

(識別子は、英字で始まる文字列です。その他の文字は英数字またはASCIIアンダーライン('_')でなければなりません。アセンブラにとって意味を持つのは最初の8文字のみですが、それ以上の文字を入力することもできます。)

つまり。 TILTOWAIT と書かれた名前のうち、識別されるのは先頭の 8 文字 TILTOWAI までで、ソースコードTILTOWAIT と記述してもコンパイル上は TILTOWAI として扱われてしまいます。

コンパイル時のメモリ消費などを抑えるために、識別子を短くする傾向にあったのは覚えていましたが、処理系の制限は完全に忘れていました。

INCLUDE FILE MECHANISM

初期の Pascal はモジュールの機構がなく、分割コンパイルができませんでした。 すべてのコードを一つのプログラムとして書く必要がありました。

とはいえ、大きなファイルを扱うのが大変なのは今も昔も変わりません。 むしろ、テキストエディタ等の性能を考えると大きなファイルを扱うのは一層大変だったと思います。

計測してみると、一番大きなファイルでもサイズは 30,224 バイト。 もしかすると 32,767 (7FFFh) バイトあたりに壁があったのかもしれません。

プログラムは分割できない。 ファイルは大きくできない。 そんな制限の中でプログラミングするにはどうしたらよいか。

そこで登場するのが、別のファイルを丸々取り込んでしまう INCLUDE FILE MECHANISM です。

The syntax for instructing the compiler to include another source file into the compilation is as follows:

(*$IFILENAME*)

考え方は C や C++#include と同じです。 通常はヘッダファイルを指定しますが、制限はあるわけでなく、他のソースファイルを丸ごと include することは可能です。

この機構を使って、別ファイルに分割したコードを読み込み一つのプログラムを組み立てています。

言語仕様でサポートされていない仕組みを、処理系の機構でカバーしている例です。

Wiz1A.DSK/WIZ.TEXT.txt:L444

(*$I WIZ1A:WIZ2.TEXT *)

ところが。 1979 年当時の UCSD Pascal のマニュアルを読んでみると、その後の Pascal に装備された unit というモジュール化のための仕組みが拡張仕様として記載されていました。

unit の仕組みを利用せず、include の仕組みを利用したのは、当時まだ新しかった新しい仕様を単に避けただけなのか、他の処理系を利用することも考慮されていたためなのか、残されたコードだけから推しはかることは難しそうです。

古いコードを読む意義

かつて Delphi をメインにプログラミングをしていたこともあり、古い Pascal のコードを面白く読むことができました。

とはいえ。 当時の環境を掘り起こす考古学的な面白さはありますが、技術的には今後役に立つことはまずないと思います。

それでも、かつてプレイした(実際には移植版なのでこのプログラムで動作していたわけではないものの)ゲームのソースコードを読めるとなると、心踊るものがあります。 この辺の感覚が、いまだに飽きずにプログラミングをしている理由なのかもしれません。

いつか読むはずっと読まない:完結篇

以前、結城先生のサイトで LaTexソースコードとともに公開されていた頃から読んでいた数学ガール。 とうとう完結です。

…。 この一つ前の巻を読んでいないような気がしてきた。

*1:Wizardry の第一作。邦題「ウィザードリィ 狂王の試練場」