2026-04-09

rkubootのbufは、もうひとつ欲しい

SIMHのPDP-11エミュレータを使ってUNIXv6について調べています。RK05にインストールされたカーネルを起動するには、ディスクの先頭にあるブートローダ「rkuboot」が使われています。これの主な処理は「fsboot.s」にあります。これはファイルシステムを解釈して、指定されたカーネルのiノードを取得し、その中で参照されているディスクブロックをメモリ上にロードします。UNIXv6のカーネルは、今の目から見れば小さいですが、さすがに直接参照だけでは足らないので、間接参照が使われています。

 

処理の概要は、間接参照のディスクブロックをひとつずつ見ていき、その指し示しているディスクブロックを順番にメモリ上に持ってきます。ここで気になるのが、間接参照のディスクブロックを見る際も、カーネルに相当するディスクブロックを得る際も、512バイト分として確保されている領域「buf」を使っていることです。

 

カーネルに対応するiノードは、メモリ上に確保された「inod」に置かれますから、特に問題はありません。そのiノードの中から間接参照によるディスクブロック番号を得るために、「buf」を使用します。そしてカーネルの一部があるディスクブロック番号が判明すると、その内容を「buf」に持ってくるのですが、こうすることで、先ほどの間接参照として使っていた内容は上書きされてしまいます。「buf」にあるのはカーネルの一部ですから、それをメモリのしかるべき位置にコピーしています。カーネルの次のディスクブロックを得るには、再びiノードを参照して間接参照によるディスクブロック番号を取得する必要がありますが、「buf」には情報がありませんから、またしてもrmblkで読み込む必要があります。この繰り返しが、カーネル全体を読み込むまで続きます。

 

ひとつの「buf」を使いまわしているから、こうなってしまうのではないかと思います。UNIXv6当時のCPUやメモリリソースが限られていたとしても、「buf」ひとつだけで処理しないで、もうひとつあれば良いのにと思うところです。 

rkubootにおける直接参照も間接参照も、カーネルをブートできれば良いと割り切っている

SIMHのPDP-11エミュレータを使いUNIXv6について学んでみようと思っています。マシンが起動され、RK05にインストールされたカーネルがメモリ上にロードされるには、ディスクの先頭にあるブートローダ「rkuboot」が使われます。この主処理は「fsboot.s」にあります。このローダがファイルシステムを解釈して、カーネルを探して、メモリ上に配置し、制御を移すことで、カーネルが動き出します。

 

ファイルシステムを解釈するには、iノード、ディスクブロックの直接参照や間接参照などを取り扱う必要があります。その処理はルーチン「rmblk」にあるようです。UNIXv6当時のファイルシステムは、今日とは異なり、「LARGEフラグ」をみて、直接参照と間接参照を切り替えていたようです。さらにコメントには「huge algorithm is not implemented」とあり、二重間接参照は実装していません。

 

ブートローダの目的は、UNIXのファイルシステムを正確に取り扱うことではなく、カーネルさえ読み込めれば十分であるという割り切りがあるようです。そうであるなら、当時のカーネルは小さなファイルだったので、さすがに直接参照では扱えませんが、間接参照だけで読み込める程度の大きさだったのでしょう。二重間接参照が必要となるほど大きなファイルにはならなかったので、それを実装したところで絶対要らないのが明らかですから、余計なことはしていないのでしょう。

 

しかも、直接参照にしろ間接参照にしろ、ディスブロックとして得られた値が「0」になっていれば、そこで読込みを終えることになります。そのためのロジックは入っていますし、「bno = buf+514.」のように、何故か512+2が指定されていることからわかるように、それなりに考慮はされているようです。しかし、直接参照にしろ間接参照にしろ、いかなる場合も絶対に問題が起きないように念入りに対処するというよりは、「カーネルを読むだけだから、細かいところは気にしなくても良いだろう」と割り切っているのではないかと思われるロジックに見えます。

 

ブートローダは512バイト以下におさめる必要がありますから、エラーチェックをしっかりとか、ファイルシステムを完璧に実装しようなどという理想に走ると、容量をオーバーしてしまうでしょう。問題があることはわかっているけど、仕方ないよねという割り切りを感じるのです。 

リッチーとトンプソンによる「The UNIX Time-Sharing System」は2つある

PDP-11で動作したUNIXv6について学ぼうとすると、真っ先に挙げられるのは「Lions本」と呼ばれる『Lions' Commentary on UNIX』です。僕自身も、アスキー出版局から出た和訳を参考にしています。その中には、次のような箇所があります。

  • 『The UNIX Time-sharing System』はオリジナルの『Communications of the ACM』の論文の改訂版である。1か月に少なくとも一度は再読すべきである。

  

この一文の前半にある「論文の改訂版である」というのは、一読しても何のことか分かりませんが、後半の「再読すべきである」は、よく分かります。暗記してしまうくらい熟読せよという意味でしょう。これはネットを検索すれば見つかるので、手元に置いておき、仰せの通りに何度も読み直そうと思います。

 

UNIXv6のブートローダを調べていると、ファイルシステムの構造として「LARGEフラグ」というものがあり、それに応じて、直接参照と間接参照を切り替えていることを知りました。今日でもファイルシステムでは直接参照と間接参照がありますが、「LARGEフラグ」というものは存在しません。何時ごろ無くなったのだろうかとGeminiに尋ねてみたら、UNIXv7で変わったとの回答を得ました。ということは、LARGEフラグがあるのはUNIXv6が最後ということになります。

 

しかもGeminiがいうには、「The UNIX Time-Sharing System」には、1974年のCACM版と、1978年のBSTJ版があるそうです。その「4 Implementation of the file system」の記述内容が大きく変わっているとのことでした。あらためてネットから入手し直してみると、タイトルも著者も同じですが、内容が異なる2つの論文があることを確認しました。

 

UNIXの系統樹などを見ると、BSD版はUNIXv6から派生したように書かれています。直接的にはそうなのかもしれませんが、UNIXv7で加えられた変更をBSDが取り込んだタイミングがあると思います。それは何時だったのでしょうか。BSDの旧いソースを追いかけると、わかるかもしれません。 

rmblkはリターンアドレスを操作する

SIMHのPDP-11エミュレータを利用し、RK05にインストールされたUNIXv6のブートローダ「rkuboot」について調べています。これは「run」により作られますが、主たる処理は「fsboot.s」にあります。この中にあるルーチン「rmblk」は、サブルーチン呼び出しのリターンアドレスを操作しているようです。

 

rmblkが呼び出されると、まず「add $2,(sp)」があります。このままサブルーチンを終える場合もありますが、場合によっては、戻る直前に「sub $2,(sp)」しています。一方でrmblkの呼び出し元では「jsr pc,rmblk」の直後に「br callout」と「mov $buf,r1」が置かれています。普通なら、JSR命令で呼び出したサブルーチンから戻ってくると、その次の命令(ここであれば、BR命令)が実行されることになります。しかしrmblk側でリターンアドレスを操作しているので、戻り方に依っては命令を飛ばし(BR命令は実行されず) 、MOV命令から実行されます。かなり職人芸と言える技だと思います。

 

PDP-11上でUNIXv6が動作していた時代は、現在から比べると、CPU性能もメモリ容量も極めて限られていたので、このような芸当を駆使していたのだと説明されることがあります。確かにその通りかもしれません。そうであったとしても、このような芸当に走るのは、ちょっとやりすぎではないかと思わないでもありません。rmblkが正常終了したか異常終了したかに依って処理を分岐させたいのであれば、他の方法もあるだろうし、そうすることが困難だとも思えません。 

rkubootにはmbootのような行入力の特殊文字がない

SIMHのPDP-11エミュレータを利用し、「SETTING UP UNIX - Sixth Edition」における「Making a Disk From Tape」から「Booting UNIX」の動作を追体験しています。配布されたテープイメージをディスクにコピーする段階では、テープの先頭にあるmbootにより、ディスクからブートする段階では、ディスクの先頭にあるrkubootにより、キー入力された名前のプログラムを探し、それに制御を移しているようです。mbootもrkubootも、Geminiに助けてもらいながら、ロジックの詳細は理解できました。

 

mbootもrkubootもスクリプト「run」で作られます。mbootの中心となるのは「tpboot.s」で、rkubootの中心となるのは「fsboot.s」で、どちらも同じような作りになっています。ただしよく見ると、tpboot.sでは、キー入力を間違った場合の簡易編集機能として「@」や「#」を扱う処理が入っています。しかしfsboot.sにはありません。

 

mbootにしろrkubootにしろ、キー入力時に簡易編集機能が必須というわけではないと思います。あればあったで便利かもしれませんが、なければなくても別に困らないと思います。また簡易編集機能を処理に組み込んだとしても、些細なロジックですから、ブートローダの512バイト制限を気にするほどのことではないと思います。

 

おそらく簡易編集機能は、あってもなくても構わない程度の、おまけ機能なのでしょう。 

2026-04-08

rkubootの動作

SIMHのPDP-11エミュレータを使用してUNIXv6について学んでいます。同様の試みはオンラインでも書籍でも数多く存在していますが、カーネル本体がメモリ上にロードされた以降を対象にしているようです。もちろんそれで何の問題もないのですが、いきなりカーネルを学ぶよりも、助走のつもりで、ディスク上のブートローダがカーネルをメモリに読み込むところから調べてみることにしました。UNIXv6のブートローダは、ディスク毎に分かれているようですが、RK05用のrkubootを対象にします。

 

rkubootはスクリプト「run」で作られています。主要部分は「fsboot.s」です。ブート手順については「SETTING UP UNIX - Sixth Edition」の「Booting UNIX」に書かれているとおりです。まずPDP-11のフロントパネルからブートローダをメモリ上に置くプログラムを入力し、実行します。そして読み込まれたブートローダを実行すると、プロンプト「@」が出力されるので、カーネル名称として(例えば)「rkunix」を入力します。こうするとブートローダがカーネルを読込み、制御を移し、カーネルが動き出します。

 

ブートローダ「rkuboot」の概要は上述したとおりですが、「fsboot.s」を追いかけてみると、次のような処理をしていました。

  1. 0番地に置かれている自分自身を137000番地に移す。こうしておかないと、カーネルを読み込もうとした際に、ブートローダ自身が上書きされて壊れてしまいます。
  2. プロンプト「@」を出力し、カーネルのパス名を入力させまる。ここで、mbootにあったような特殊文字「#」や「@」のハンドリングはありません。またカーネルが階層ディレクトリに置かれている場合でも対処できる処理になっています。
  3. 入力されたカーネル名称をファイルシステムから探します。FILE SYSTEM(V)にも明記されているとおり、i番号の「1」がルートディレクトリなのが決まりですから、それを前提に探します。そしてルートディレクトリ(またはサブディレクトリ)に指定されたカーネルが見つかれば、そのi番号をからi-nodeを参照してディスク上のカーネルをメモリに読み込みます。

 

ブートローダですから、上記した内容が全てですが、これらの処理を512バイト以内に納める必要があります。あまり複雑な処理とか、エラーチェックとかをしている余裕がないようで、ある種の「割り切り」があるように思います。例えば、fsboot.sのコメントには「huge algorithm is not implemented」とありますが、これは今日のUNIXにもある「二重間接参照」は実装されていないということのようです。実際問題として、二重間接参照が必要となるほどカーネルは大きくないので、実装しなくても実用上問題ないという「 割り切り」があるのでしょう。他にもエラーチェックなどをしない箇所がありますが、それも「普通そういう事態にはならないはず」という「割り切り」があるのだと思います。

2026-03-28

「Making a Disk From Tape」と「Booting UNIX」の対称性

SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」と「Booting UNIX」は、手順が対称的になっていることに気づきました。

 

「Making a Disk From Tape」では、細部を省略すると、次のような手順です。

  1. TU10コードをフロントパネルから入力して実行する。
  2. mbootがロードされるので、実行されると、プロンプト「=」が出力される。
  3. tmrkなどを入力することで、テープからディスクにコピーが行われる。

 

これに対して「Booting UNIX」では、次のような手順です。

  1. RK05コードをフロントパネルから入力して実行する。
  2. rkubootがロードされ、実行されると、プロンプト「@」が出力される。
  3. rkunixなどを入力することで、ディスクからカーネルが起動する。

 

「Making a Disk From Tape」の処理を追いかけてくることで、UNIXv6が動作する以前の世界が理解できました。

RK05は2種類ある

「RK05 disk drive maintenance manual」(DEC-00-HRK05-C-D)に記載されている「Table 1-1 Performance Specifications」には「Sectors(records)」として「4872 (12 per revolution)/6496 (16 per revolution)」とあります。これは何でしょうか。

 

RK05は、片面203シリンダ(含予備)なので、両面ならば406トラックあります。406×12=4872ですし、406×16=6496です。計算は合っていますが、どういう意味でしょうか。Geminiに相談したら、RK05は、1シリンダが12セクタの製品と16セクタの製品が存在したのだと回答しました。そうなのかもしれません。しかし裏付け資料が見つからないので、真偽が定かではありません。

 

UNIXv6の「rk.s」には、「div $12.,r0」という処理があります。この結果がRKDAレジスタ(777412番地)に入ることになるので、12セクタのための処理なのでしょう。しかし、もし16セクタのRK05があるとするならば、その容量を活かせていないことになります。

 

RK05に12セクタ版と16セクタ版が存在すると仮定して、UNIXv6が自動判別する処理を組み込むのは、当時の歴史的事情としては許されなかったのではないかと推測します。どちらかい決め打ちするのであれば、12セクタ版にしておいた方が無難と判断したのかもしれません。 

 

 

tm.sのtreadとrk.sのwblk

スクリプト「run」で作られる「tmrk」の処理の中心は「mcopy.s」にあります。最も重要なのは、「tread」と「wblk」を呼び出して、512バイト(256ワード)のコピーを指定回数分だけ繰り返しているところです。

 

「tread」は「tm.s」にあります。「wblk」は「rk.s」にあります。両者を見比べると、テープを操作するかディスクを操作するかという事だけではなく、大きく違うのですが、論理的には対称になっているはずです。「wblk」の方がシンプルで、「tread」は複雑です。その複雑さに目を奪われてしまうことなく、対称性に注意すれば、「tread」が何をしようとしているのか見えてくると思います。 

mcopy.sにおいて「illegal digit」が出力された場合の挙動

SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」の手順に従って「tmrk」を呼び出すと、「disk offset」、「tape offset」、「count」の入力が求められます。これは「mcopy.s」のルーチン「numb」が呼ばれることで、キーボードから入力された数値がR0に入ります。ここで数字以外を入力すると「illegal digit」というエラーメッセージを出力して、「rts pc」が実行されます。ここで気になるのは、一体何処にもどるのでしょうか。

 

「tmrk」の中で「jsr pc,numb」として呼び出されている訳ですが、「tmrk」の中に戻るわけではないようです。エラー発生時に処理を追いかけていくと「tst (sp)+」をした後で「rts pc」となっています。この「tst (sp)+」が重要です。オペコード「TST」というのは、オペランドを評価してフラグをセットする命令です。普通は直後に条件分岐命令が続きます。しかしここでは「rts pc」が続いていて、フラグの状態には影響されません。

 

要するにここでは、TST命令である必要はなく、「(sp)+」として、SPが変化する事が重要なのです。SPを変化させることで、「tmrk」から呼び出された際の戻り番地を捨て、その上位にある「mboot」に戻るためのテクニックだと思います。そうなら、それが分かりやすいプログラムを書けば良いではないかと思うところですが、こういう職人芸的なプログラムを書くところが当時の時代背景としてあったのでしょう。 

「SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」におけるmbootとtmrkの関係

SIMHのPDP11エミュレータを利用し、UNIXv6について学ぼうと考えています。同様の志を持つ方は少なくなく、オンラインでも書籍でも数多くの情報があります。しかしながら、それらはUNIXv6がディスクにインストールされていることを前提としており、「SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」について言及されていないように思います。それでも別に構わないのですが、いきなりカーネル本体に取り組むより、例え多くの情報が蓄積されているとしても、まずはPDP-11のCPU本体や周辺機器の扱いに慣れる意味で、「Making a Disk From Tape」の手順や、その処理に精通しておくことにしました。

 

「Making a Disk From Tape」では、TU10の先頭ブロックをロードするコードが提示されています。PDP-11実機であれば、これをフロントパネルから入力し実行することで、配布テープの先頭ブロックがメモリにロードされます。これは、スクリプト「run」で作成される「mboot」であることを突き止めました。これが実行されると、プロンプト「=」が出力され、入力待ちになります。導入手順では「tmrk」と入力することになっていて、これをおこなうことで、配布テープからディスクにUNIXv6がコピーされます。

 

「mboot」も「tmrk」もスクリプト「run」によって作成されるa.out形式ファイルです。しかし両者は独立している訳ではないようで、「mboot」が主、「tmrk」が従の立場で動いているようです。このような主従関係と考えるのは、以下のような挙動だからです。

 

まず「mboot」が0番地からロードされ、実行されると、まず最初に自分自身を137000番地に移して、以降はそちらで実行を続けます。これは、「tmrk」などが0番地からロードされた際に、「mboot」が壊されないためと思われます。この挙動から、「mboot」が主で、「tmrk」が従と見做せます。 

 

さらに「mcopy.s」のでは、文字列を出力するため「jsr pc,4(r5)」のようなサブルーチン呼び出しが存在します。ここでR5は、「tmrk」では設定されていません。「mboot」の方で設定されていることが前提となっています。このような前提で「tmrk」が動作している事実が、主従関係があると判断する理由です。

 

また「tmrk」が実行されると、「disk offset」、「tape offset」、「count」の入力を求め、その入力に応じてテープからディスクにコピーすると、実行を終了してしまいます。実行終了後にどうなるかと言うと、「mboot」に戻るのです。この挙動を見ても、「mboot」が主で、「tmrk」が従だとわかります。

 

「Making a Disk From Tape」の手順6では、「tmrk」の代わりに「htrk」、「tmrp」、「htrp」などを使う場合の記述があります。これらのプログラムは調査していませんが、おそらく「tmrk」と同様だろうと思います。 

2026-03-22

mbootとUnix-v6-Ken-Wellsch.tap

SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」で行われた処理について調べています。ここで書かれている手順の概略は次の通りです。

  1. PDP-11のフロントパネルを操作し、100000番地からTU10コードを入力し、実行する。
  2. テープから先頭ブロックが0番地に読み込まれるので、実行する。これは「mboot」です。
  3. 「mboot」の中でプロンプト「=」が出力されるので、テープの内容をディスクに書き込むプログラム名を指定する。ドキュメントでは「tmrk」が指定されています。「mboot」は、指定されたプログラム「tmrk」をテープから探し、メモリにロードし、制御を移します。
  4. 「tmrk」が実行されると、指定位置から指定長をテープから読み込み、ディスクに書き出します。

 

UNIXv6が出た当時は、これらの手順を実機PDP-11/40などでおこなったのでしょうけれども、21世紀に生きる我々はSIMHのPDP11エミュレータを使います。そしてテープイメージには、ファイル「Unix-v6-Ken-Wellsch.tap」を使います。

 

「mboot」は、スクリプト「run」によると、「tpboot.s」、「tty.s」、「tm.s」からできています。これらのソースを参照し、SIMHのPDP11エミュレータのデバッグ機能を駆使し、時々Geminiとの対話を繰り返しながら、解析しました。なんとか「mboot」の動作は理解できた気がします。動作の概略は、以下のようになっています。

  1. TU10コードにより、「mboot」はメモリ番地0からロードされます。「mboot」の目的は、ユーザが指定した「tmrk」などのプログラムをメモリ番地0からロードして制御を渡すことなので、このままではよくありません。そこで自分自身を上位に移し、プログラムがロードされる場所を空けておきます。上位のメモリ番地は137000のようです。
  2. プロンプト「=」を出力し、ユーザからプログラム名を入力してもらい、それをテープから検索します。
    1. プログラムを入力する際、「@」が入力されると、それまでの入力が全て取り消されます。また「#」が入力されると、その直前の入力が取り消されます。
    2. 入力された文字列(「tmrk」など)がテープに存在するか検索します。テープファイル「Unix-v6-Ken-Wellsch.tap」を調べたところ、先頭ブロックには「mboot」が格納されていますが、その次には「hboot」が入っていることがわかりました。その次からTP(V)形式のディレクトリが続きます。PDP11のデバッグ機能を使って動作を確認すると、「hboot」が格納されているブロックから検索を始めていることが分かりました。TP(V)形式のディレクトリではないし、そもそも何故「hboot」がテープに置かれているのか分かりません。 
  3. テープ上の場所が分かったら、それをメモリ番地0からロードして、制御を渡します。ただしロードする前に、メモリを全て0クリアしています。未初期化の変数を参照してしまった場合、再現不能なバグが出ないための事前準備でしょうか。

 

テープファイル「Unix-v6-Ken-Wellsch.tap」を解析すると、次のようになっていました。

  1. Block#0は、a.out形式の「mboot」が格納されている。
  2. Block#1は、a.out形式の「hboot」が格納されている。
  3. Block#2からは、TP(V)形式のディレクトリ情報が格納されている。格納されているプログラムは、次の通りです。
    1. 「dldr」(size:000000344,tape:000031)
    2. 「dtf」(size:000004546,tape:000032)
    3. 「hboot」(size:000000724,tape:000037)
    4. 「hpuboot」(size:000000766,tape:000040)
    5. 「hthp」(size:000000624,tape:000041)
    6. 「htrk」(size:000000554,tape:000042)
    7. 「htrp」(size:000000566,tape:000043)
    8. 「mboot」(size:000000674,tape:000044)
    9. 「mcopy」(size:000000660,tape:000045)
    10. 「mem」(size:000012756,tape:000046)
    11. 「reset」(size:000000024,tape:000061)
    12. 「rkf」(size:000000200,tape:000062)
    13. 「rkuboot」(size:000000716,tape:000063)
    14. 「rpuboot」(size:000000730,tape:000064)
    15. 「tboot」(size:000000626,tape:000065)
    16. 「tcf」(size:000004516,tape:000066)
    17. 「tmhp」(size:000000574,tape:000073)
    18. 「tmrk」(size:000000524,tape:000074)
    19. 「tmrp」(size:000000536,tape:000075)
    20. 「uboot」(size:000001000,tape:000076)

2026-03-19

Unix-v6-Ken-Wellsch.tapの構造

SIMHのPDP11エミュレータを使ってUNIXv6を学ぼうと考えています。同様の志を持つ人たちが、ネットでも書籍でも多くの情報を提供しています。ただしUNIXがインストールされた環境を前提にしていることが多いように見受けられます。カーネルを学ぶには、インストールが済んだことを前提にする方が良いのだと思いますが、PDP-11について学ぶ練習のため、「SETTING UP UNIX - Sixth Edition」の手順でインストールするところから調べてみようと思います。

 

 SIMHで利用できるテープイメージのファイルが「Unix-v6-Ken-Wellsch.tap」として入手できます。これはSIMHで扱える形式になっているようです。その構造は「SIMH Magtape Representation and Handling」という資料があります。この構造を確認するため、Pythonでスクリプトを組んでみました。

#!/usr/bin/python3

def readblock(f):
    global TapeMark
    global Block
    Block += 1
    len0 = int.from_bytes(f.read(4),'little')
    if len0 == 0:
        TapeMark += 1
        if TapeMark >= 2: raise EOFError
        return 0
    TapeMark = 0
    data = f.read(len0)
    len1 = int.from_bytes(f.read(4),'little')
    print("[%6d]:" % Block, TapeMark, len0, data[:4])
    return 0 if len0 != len1 else len0

TapeMark = 0
Block = 0

with open("Unix-v6-Ken-Wellsch.tap","rb") as f:
    while True:
        try:
            readblock(f)
        except EOFError:
            break
#[EOF]

 これで確認すると、512バイトのブロックが12,100あります。ドキュメントに書かれているとおりです。

 

また先頭ブロックには「mboot」があります。次のブロックには「hboot」が置かれています。その後には「TP(V)」形式のディレクトリが続きます。ブートプログラムとして「mboot」と「hboot」の二つが用意されている理由はなんでしょうか。ドキュメントでは、フロントパネルから入力する「TU16」のプログラムが「(To be added)」となっています。このテープの配布を受けた側がTU10でもTU16でも対応できるようにしておいたのかもしれません。

2026-03-16

SDカードは劣化すると「書込み禁止状態」に入るらしい

スマホで写真を撮影しても、撮ったはずの写真が残っていない現象に遭遇しました。「カシャ」という音はするのですが、ファイルが残っていません。アプリの設定を確認すると「SDカードに記録する」という項目があり、「ON」になっていたので、「OFF」に切り替えたところ、問題が解決したようです。

 

スマホで使っているSDカードは、随分前にガラケーで使っていたものです。15年以上前に購入したものだと思います。劣化していてもおかしくはありませんが、記録されている情報は普通に読み取れるのです。最近は、Googleで似たような事例を検索してみるのではなく、Geminiに相談することが増えました。Geminiが言うには、SDカードが劣化して「書込み禁止状態」になっているのではないかとのことです。

 

SDカードが劣化すると「書込み禁止状態」にはいるという事を初めて知りました。Googleで検索してみたら、そういう状況を報告する「SDカードの「書き換え寿命」と、壊れる直前のサイレントサイン 」が見つかりました。壊れるものは仕方がないので、新しいSDカードを購入するしかありません。劣化したSDカードからファイルを救い出すことはできそうなので、完全に壊れて読めなくなる前に、新しいものと交換しようと思います。

 

2026-03-14

TU10コードからmbootを経由してtmrkが呼ばれるまで

SETTING UP UNIX - Sixth Edition」の「Making a Disk From Tape」において、手順3で入力されたコードを実行すると、テープからプログラムがロードされます。simhのPDP11エミュレータを使用し、テープイメージとして「Unix-v6-Ken-Wellsch.tap」を使用する場合、このロードされたプログラムは「mboot」だと思われます。これは「run」の中で、以下のようにして作られています。

as tpboot.s tty.s tm.s
strip a.out
ls -l a.out
cp a.out /usr/mdec/mboot 

 

そして手順5において、プロンプト「=」が表示され、「tmrk」を入力するように指示されています。

 

この一連の処理を追いかけているのですが、「tpboot.s」では、プロンプト「=」を出し、「TP(V)」形式のテープから指定されたプログラム(例えば「tmrk」)を読みだして、制御を移しているだけです。 

 

つまり、「Making a Disk From Tap」では、次のような段階を踏んで、テープにあるUNIXv6をディスクにコピーしていることになります。

  1. フロントパネルからTU10コードを入力し、実行する。
  2. テープの先頭ブロックにmbootが入っており、それを実行する。
  3. mbootがプロンプト「=」を出し、コマンド「tmrk」などを入力すると、mbootがテープから探し出し、メモリ上にロードする。
  4. メモリ上にロードされたプログラムが、テープからUNIXv6本体をディスクにコピーする。

 

 処理の流れが見えてきました。これだけわかれば十分とも言えますが、mbootに含まれるtpboot.s、tty.s、tm.sは、PDP-11のアセンブラで書かれていますが、短いので、今後UNIXv6の本体を追いかけていく練習として、ロジックを細部まで見ていこうと思います。

2026-03-09

さぁ、ここからはピッチを調節して、あなたが操縦して着陸してください

Windows11でMicrosoft Flight Simulator 2002が動くようになったので、紹介ビデオの直後のフライトに挑んでいます。操作は、ジョイスティックを所有していないので、全てキーボードでおこないます。このフライトは、ゲーム購入直後におこなうものだと思います。飛行用語も操作方法も全く知らずにおこなうので、滅茶苦茶な操縦をしながら、離陸から着陸までを体験します。これがシミュレータなので事故も怪我もありませんが、いきなり飛び立ち着陸させるなんて、本当の飛行訓練ではありえない状況だと思います。

 

離陸は比較的簡単です。エンジンを全開にしてから機首を上げると、離陸できます。空中では、右旋回や左旋回を体験しますが、教官が代わって操縦してくれます。そこまでは、手取り足取りなのに、着陸だけは、「さぁ、ここからはピッチを調節して、あなたが操縦して着陸してください」と言われてしまいます。

 

このソフトを使う人の中には、操縦に関する知識があり、何をすべきか熟知していることもあるでしょう。しかし私は素人なので、何をしたらよいのか、さっぱりわかりません。着陸操作をしろと命じられたら、じゃぁエンジンを切ればよいのだろうかと考えてしまいますが、それでは墜落してしまうでしょう。ならば機種を下に下げれば良いのかとも思いますが、それでは失速してしまいそうです。要するに、何をしたらよいのかわかりません。それなのに教官は「ピッチを操作し、速度を65ノットに保ち、着陸してください」と簡単に言います。もちろんシミュレータですから、操作が酷くても事故にはつながらないのは、十分に承知していますが、困ってしまうのは確かです。

 

飛行機というものは、XYZ軸に応じて、「ヨー」、「ロール」、「ピッチ」という言い方をするようです。教官の言う「ピッチを調節して」というのは、このことを言っているのだろうかと、つい最近気づきました。混乱するのは、計器類の中にピッチトリムというものがあり、教官はこれを操作しろと言っているんだろうかと誤解するのです。

 

何度か離陸着陸を繰り返しているうちに、だんだんわかってきました。しかし空港の滑走路に着陸できたことは一度もなく、建物や樹々に激突します。しかし機体にも自分自身にも何の怪我もないのが、シミュレータですね。

2026-03-07

mboot

UNIX V6のセットアップをおこなうドキュメント「SETTING UP UNIX - Sixth Edition」によると、配布テープは「The tape contains 12100 512-byte records followed by a single file mark」だそうです。さらにオフセット100から4000レコードが「バイナリ」、その後の4000レコードが「ソース」、その後の4000レコードが「ドキュメント」との事です。全体として12,100レコードということは、オフセット0から100レコードには、テープを読みこみディスクに書き出すユーティリティが格納されていることになります。

 

ドキュメントの「Making a Disk From Tape」では、TU10コードにより、テープのオフセット0から数レコードを読んでメモリの000000番地以降に展開しています。ここで何を読み込んだのか不明ですし、どのような処理が行われた結果としてプロンプト「=」が出るのか、霧の中です。もっとも、この過程がわからなくても、UNIX V6のディスクを作ることはできます。しかも、わかったところでUNIX V6の本体の理解が進むわけでもありません。そうであったとしても、何故この処理を理解しようとするのかと言えば、今後UNIX V6を学んでいくための準備練習として最適ではないかと考えるからです。

 

TU10コードを実行した後からプロンプト「=」が出るまでの間を理解するため、Googleを検索したり、Geminiに相談したりしましたが、確たる情報は得られませんでした。そうならば、発想を変えて、TU10コードが実行された後のメモリがどうなっているかを調べてみようと思い至りました。「Installing UNIX v6 (PDP-11) on SIMH」で使われている「Unix-v6-Ken-Wellsch.tap」を使用してTU10コードを実行してみました。その後simhのPDP11エミュレータで「e -m 000000-001000」することで、1レコード分を逆アセンブルしてみました。その内容を確認してみると「mboot」であることが確認できました。

 

UNIX V6のソースを確認すると「source/mdec/run」には、次のような箇所があります。

as tpboot.s tty.s tm.s

strip a.out

ls -l a.out

cp a.out /usr/mdec/mboot 


ソースには「source/mdec/tpboot.s」、「tty.s」や「tm.s」もありますから、逆アセンブルした結果を全て確認したら、まさに「tpboot.s」、「tty.s」、「tm.s」そのものでした。これらをアセンブルしたものが「mboot」というバイナリで、これはa.out形式です。サイズは440バイトだったので、1レコードに収まります。ここから考えても、TU10コードでは数レコードを読み込もうとしていますが、1レコードを読み込むだけで十分であることが分かります。

 

次はmbootの処理を追いかけてみようと思います。 

 

 

 

 

UNIX V6の配布テープを読み込む処理

UNIX V6を通してOSについて深く学ぼうと考えています。simhのPDP-11エミュレータを使用しようと考えています。このようなアプローチは、以前よりオンラインでも書籍でも多くの情報があります。それらは、ほとんどの場合において、UNIX V6がディスク上にインストールされていることを前提としています。しかし「SETTING UP UNIX - Sixth Edition」を読むと、前段階として「Making a Disk From Tape」が必要です。これらはOSの挙動とは関係ありませんが、PDP-11の動作を学ぶには格好の素材ではないかと思います。そういうつもりで、この処理を詳しく調べてみることにしました。

 

まず最初におこなうのは、TU10コードを(本物のPDP-11/40なら、フロントパネルから)入力し、テープから「何か」を読み込むことです。ドキュメントにはバイナリコードが記載されていますが、理解しやすくするため逆アセンブルしておきます。これが100000番地から格納されることになります。

100000: MOV #172526,R0

100004: MOV R0,-(R0)

100006: MOV #060003,-(R0)

100012: BR 100012 

 

100000番地で、R0に定数「172526」を代入しています。これは100004番地の処理をするための、準備と言えます。100004番地では、代入先が「-(R0)」となっていますから、まずR0をデクリメントし、そのR0が指しているアドレスに何かを(ここではR0の内容)を代入しています。R0は172526だったのですから、デクリメントすると172524になります。これはTM11のMTBRCレジスタです。代入される値もR0ですから「172524」です。

 

ドキュメント「TM11 DECmagtape system」(DEC-11-HTMAA-D-D)によると、「4.2.3 Byte Record Counter (MTBRC)」で説明されていますが、転送量の2の補数を指定します。ここでは「172524」を指定していますが、これは転送量を2の補数にしたものなので、本当の転送量は、逆算して、「2732」となります。読み取り操作では、ワード数で指定するようです。テープは512バイトを1レコードとしていますが、2732ワードならば、10レコード以上あるし、しかも中途半端です。何故中途半端な値を指定するのかは不明ですが、推測するに、フロントパネルから入力するロジックを短くしたかったのでしょう。おそらく本当に必要なのは1レコードだったと思われますが、それを忠実にロジックに落とすと、ちょっとだけ長くなるので、それを避けたかったのでしょう。今日であれば気にするほどのことではないと思いますが、当時の時代背景を踏まえると、重大問題だったのだと思います。

 

100006番地では、さらにR0をデクリメントしていますから、172522であるMTCに「060003」を代入しています。これもドキュメントの「4.2.2 Command Register (MTC)」より、「DEN8」が「1」で「DEN5」も「1」なので「800bpiの9チャンネルテープ」であり、「Function」が「001」なので「Read」となり、「GO」が「1」なので実行開始されます。

 

最後に無限ループに入りますが、現実のPDP-11/40ならば、操作する人が目視していれば、テープ装置の読取り終了が判断できるのでしょう。 

 

このようにしてテープから読み込まれた内容(おそらく1レコードで十分なはず)が000000番地から書き込まれています。ドキュメントでは、手順4として「Halt and restart the CPU at 0.」と書かれているので、読み込まれたプログラムに制御が移ります。 

PDP-11のJSR命令の挙動と時代背景

PDP-11のJSR命令は、サブルーチンを呼び出すために使われます。x86におけるCALL命令のようなものです。x86に限らず、Z80でも6800でも、同様の命令が存在します。使うには、x86なら「CALL サブルーチン」のようにします。戻り番地はスタックに積まれているので、サブルーチンでは「RET」するだけです。

 

ところがPDP-11では「JSR レジスタ, サブルーチン」のようにしています。このとき戻り番地がレジスタに入りますが、レジスタをR7(=PC) とするなら、戻り番地はスタックに積まれます。要するにx86や6800などと同じ挙動です。サブルーチンを呼び出す命令で、なぜレジスタが出てくるのか分からず、Geminiに相談してみました。Geminiの応答は、自信たっぷりに返事をしますが、必ずしも正しいとは限らないので、話半分に聞いておく必要がありますが、それなりに納得できました。

 

Geminiが言っていたのは、PDP-11が登場した頃の時代背景が理由として考えられるそうです。その要点は、つぎのようなものでした。

  1. 当時は、アーキテクチャとしてスタックが今日のように一般的ではなかった。
  2. サブルーチン呼出し命令の直後に引数を置いておくテクニックが存在した。
  3. コードとデータの分離が不完全で、職人技的技法としてコードを書き換えることもあった。 

 

x86などでは、サブルーチンに与える引数は、レジスタに持つことが多く、少なくともCALL命令の直後に置いておくことはありません。しかも、8080の頃ならいざ知らず、80386以降のプロテクションモードの時代では、実行中にコードを書き換えることはなく、そんなことをしたら保護違反になってしまいます。

 

手元に、共立出版が1978年に発行した『マイクロコンピュータのプログラミング』という本があります。この中で「プログラム技法」(中西正和)では、「2.6 命令の中に命令を作る」として、今日では考えられないテクニックが紹介されています。ここで書かれているのは、バイナリ列「01 FE FF」 があったとすると、先頭から見れは「LXI B,0FFFEH」だが、2バイト目から見れば「CPI 0FFH」と見做せるし、3バイト目から見れば「RST 7」と見做せるというものです。まさに職人技です。そうかもしれないけど、そこまでするかという感じです。このようなテクニックがもてはやされるのは、メモリも少なく、(今日からすると)CPUも遅いという時代背景があるためだと思います。

 

PDP-11のJSR命令における不思議なレジスタの存在も、当時のプログラミング事情として「インライン引数」というものが存在したという時代背景が根底にあるようです。このような事情を知らないと、PDP-11のJSR命令の挙動が理解できない事を学びました。

洗濯時間はこれほど短かったのか

先月、自宅で使用している洗濯機がエラーを出しました。「ピーピーピー」という警告音が鳴った後、操作パネルのLEDが全て点滅していました。その時は、再スタートしたところ、その後は問題ありませんでした。ところが、後日洗濯機を使おうとした際にも、エラーが出てることが多くなりました。もしかしたら故障したのだろうか、買い替えるべきなのかと悩みました。

 

そもそも使用している洗濯機は、製造元が三洋電機です。購入時期は覚えていませんが、20年以上前だと思います。買い替えても良いとは思いますが、自宅で他にも故障が相次いでおり、出費がかさんでいるので、できれば買換えは避けたいところです。

 

何が悪いかわかりませんが、少なくともエラーを出す前から注水の勢いが弱いとは思っていました。水道栓と洗濯機の間が狭くて、ホースが捻じ曲がって配置されています。もしかすると水の通りが悪いのではないかと当たりをつけました。ホースを取り外し、スムースに水が供給されるように、配線を変えてみました。

 

変更した結果、力強く給水されるのが確認され、それ以来エラーも出ていません。これが原因だったのかもしれません。それだけではなく、洗濯時間も短くなりました。以前は2時間くらいかかっていましたが、洗濯量にもよりますが、今は30分ほどで終わります。あまりにも早いので、途中でエラーで止まっているんじゃないかと不安になるくらいです。 

2026-02-27

Windows11でMicrosoft Flight Simulator 2002を動かすためにdgVoodoo2を利用した

昨年秋のWindows 10のサポート終了により、Windows 11にしました。Windows 10を動かしていたマシンは、Windows 8の頃に購入したので、Windows 11のハードウェア要件を満たしておらず、新しいマシンに買い替えました。Windows 10でMicrosoft Flight Simulator 2002を動かしていましたが、特に問題はありませんでした。ところがWindows 11では、起動するし、メニュー操作などもできますが、フライト始めると数秒でプログラムが落ちてしまいます。イベントログを確認すると、メモリのアクセス違反が起きているようです。

 

MSFS2002の動作設定で「ハードウェア アクセラレーションを使用」という設定を解除できます。こうすれば、プログラムが落ちることはないのですが、画面描画がうまくいかず、フラストレーションがたまります。

 

なんとかならないだろうかと、Geminiに相談してみたところ、無事に解決しました。Geminiが提示してきた解決策は、以下の3つです。

  1. 互換モード(Windows XP SP3 / 管理者として実行)
  2. DEP(データ実行防止)の設定
  3. dgVoodoo2 の導入 

 

最終的に効果があったのは「dgVoodoo2」です。こういうものがあるとは、Geminiに教えられるまで知りませんでした。Web検索すると「dgVoodoo2でラグナロクオンラインを快適に遊ぶ」という記事が見つかりました。また解決策2は不要とのことで、最終的に解決策1と3をおこなうことで、解決に至りました。

 

MSFS2002は、名前の通り2002年に発売されているので、もう20年以上も前の製品です。Windows XPの時代です。そんな大昔のソフトウェアが、多分動かないだろうと半ば諦めていたので、藁にも縋る思いでGeminiに相談して、解決したので嬉しいです。そしてGeminiとの向き合い方も分かってきた気がします。 

2026-02-26

NFD形式ファイルをFDI形式に変換するスクリプトを作成

Windows11上のT98-NEXTでWizardryをプレイするとNFD形式でフロッピーディスクイメージが作られます。これをNetBSD/i386上のxnp2で利用するにはFDI形式に変換しなくてはなりません。NFD形式とFDI形式の構造について調べたので、NFD→FDIの変換をおこなうスクリプトを作りました。この目的はWizardryでプレイしたファイルを変換したいだけなので、大雑把な作りになっています。 

以前なら、GoogleなどでWebを検索して、参考となるサイトを探していました。今でも同様なのですが、最近はGeminiと対話しながら、作成するようになりました。世間一般では、生成されたスクリプトを何も考えずに使うだけなのか、それとも生成されたものを参考にしながら自前で作るのか、どうしているのかはわかりません。私の場合には、Geminiが生成したスクリプトを参考にして、自分で作り上げるようにしています。

その結果、以下のようなスクリプトが出来上がりました。これを使用して、T98-NEXTが出力したNFD形式ファイルを変換してみました。それをxnp2で読み込んでみましたが、問題ないようです。これでT98-NEXT(NFD)→xnp2(FDI)ができるようになったので、途中まで冒険した続きができます。xnp2が出力するファイルはFDI形式になりますが、T98-NEXTはFDI形式も読めるので、FDI→NFD変換スクリプトは作らなくても良いでしょう。

#!/usr/bin/python3
import os
import pathlib
import sys
import ctypes

# 基本的な型のエイリアス
BYTE  = ctypes.c_uint8
WORD  = ctypes.c_uint16
DWORD = ctypes.c_uint32

# セクタ情報
class NFD_SECT_ID(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("C",        BYTE),
        ("H",        BYTE),
        ("R",        BYTE),
        ("N",        BYTE),
        ("flMFM",    BYTE),
        ("flDDAM",   BYTE),
        ("byStatus", BYTE),
        ("byST0",    BYTE),
        ("byST1",    BYTE),
        ("byST2",    BYTE),
        ("byPDA",    BYTE),
        ("Reserve1", ctypes.c_char * 5),
    ]

# NFDヘッダ
class NFD_FILE_HEAD(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("szFileID",    ctypes.c_char * 15),
        ("Reserve1",    ctypes.c_char * 1),
        ("szComment",   ctypes.c_char * 0x100),
        ("dwHeadSize",  DWORD),
        ("flProtect",   BYTE),
        ("byHead",      BYTE),
        ("Reserve2",    ctypes.c_char * 10),
        ("si",          NFD_SECT_ID * (163 * 26)),
        ("Reserve3",    ctypes.c_char * 0x10),
    ]

# FDIヘッダ
class FDI_FILE_HEAD(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("Reserve1",   DWORD),
        ("dwMedia",    DWORD),
        ("dwHeadSize", DWORD),
        ("dwDataSize", DWORD),
        ("dwSectSize", DWORD),
        ("dwSecter",   DWORD),
        ("dwHead",     DWORD),
        ("dwCylinder", DWORD),
        ("Reserve2",   ctypes.c_char * (4096 - 4 * 8)),
    ]

if __name__ == "__main__":
    try:
        nfd = open(sys.argv[1], "br")
    except:
        print("usage: NFD2FDI <filename>")
        exit(1)

    try:
        nfdpath = pathlib.Path(sys.argv[1])
        fdi = open(nfdpath.with_suffix(".FDI"), "wb")
    except:
        print("cannot open FDI file")
        exit(1)

    NFDheader = NFD_FILE_HEAD()
    nfd.readinto(NFDheader)

    FDIheader = FDI_FILE_HEAD()
    FDIheader.dwMedia = 0x90
    FDIheader.dwHeadSize = 4096
    FDIheader.dwSectSize = 256
    FDIheader.dwSecter = 26
    FDIheader.dwHead = 2
    FDIheader.dwCylinder = 77
    FDIheader.dwDataSize = (FDIheader.dwSectSize
                            * FDIheader.dwSecter
                            * FDIheader.dwHead
                            * FDIheader.dwCylinder)


    try:
        nfd.seek(NFDheader.dwHeadSize)
        rawdata = nfd.read(FDIheader.dwDataSize)
        fdi.write(FDIheader)
        fdi.write(rawdata)
    except:
        print("read/write error")
        exit(1)

    exit(0)
#[EOF]

FDI形式ファイルの構造を出力するスクリプトを作成

Windows11上のT98-NEXTでWizardryをプレイするとNFD形式でフロッピーディスクイメージが作られます。これをNetBSD/i386上のxnp2で利用するにはFDI形式に変換しなくてはなりません。変換スクリプトを作成する前に、FDI形式について学ぶため、ファイル内の構造を出力するスクリプトを作ってみました。
 
以前なら、GoogleなどでWebを検索して、参考となるサイトを探していました。今でも同様なのですが、最近はGeminiと対話しながら、作成するようになりました。世間一般では、生成されたスクリプトを何も考えずに使うだけなのか、それとも生成されたものを参考にしながら自前で作るのか、どうしているのかはわかりません。私の場合には、Geminiが生成したスクリプトを参考にして、自分で作り上げるようにしています。 

その結果、以下のようなスクリプトが出来上がりました。これを使ってWizardryで使用しているファイルを参照してみると、いろいろと勉強になります。 

 #!/usr/bin/python3
import sys
import ctypes

# 基本的な型のエイリアス
DWORD = ctypes.c_uint32

# ヘッダ
class FDI_FILE_HEAD(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("Reserve1",   DWORD),
        ("dwMedia",    DWORD),
        ("dwHeadSize", DWORD),
        ("dwDataSize", DWORD),
        ("dwSectSize", DWORD),
        ("dwSecter",   DWORD),
        ("dwHead",     DWORD),
        ("dwCylinder", DWORD),
        ("Reserve2",   ctypes.c_char * (4096 - 4 * 8)),
    ]

if __name__ == "__main__":
    try:
        f = open(sys.argv[1], "br")
    except:
        print("usage: FDIdump <filename>")
        exit(1)

    header = FDI_FILE_HEAD()
    f.readinto(header)

    print(f"dwMedia:0x{header.dwMedia:08x} ({header.dwMedia})")
    print(f"dwHeadSize:0x{header.dwHeadSize:08x} ({header.dwHeadSize})")
    print(f"dwDataSize:0x{header.dwDataSize:08x} ({header.dwDataSize})")
    print(f"dwSectSize:0x{header.dwSectSize:08x} ({header.dwDataSize})")
    print(f"dwSecter:0x{header.dwSecter:08x} ({header.dwSecter})")
    print(f"dwHead:0x{header.dwHead:08x} ({header.dwHead})")
    print(f"dwCylinder:0x{header.dwCylinder:08x} ({header.dwCylinder})")

    print("header size:", ctypes.sizeof(FDI_FILE_HEAD))

#[EOF]

NFD形式ファイルの構造を出力するスクリプトを作成

Windows11上のT98-NEXTでWizardryをプレイするとNFD形式でフロッピーディスクイメージが作られます。これをNetBSD/i386上のxnp2で利用するにはFDI形式に変換しなくてはなりません。変換スクリプトを作成する前に、NFD形式について学ぶため、ファイル内の構造を出力するスクリプトを作ってみました。

 

以前なら、GoogleなどでWebを検索して、参考となるサイトを探していました。今でも同様なのですが、最近はGeminiと対話しながら、作成するようになりました。世間一般では、生成されたスクリプトを何も考えずに使うだけなのか、それとも生成されたものを参考にしながら自前で作るのか、どうしているのかはわかりません。私の場合には、Geminiが生成したスクリプトを参考にして、自分で作り上げるようにしています。

 

その結果、以下のようなスクリプトが出来上がりました。これを使ってWizardryで使用しているファイルを参照してみると、いろいろと勉強になります。 

 

#!/usr/bin/python3
import sys
import ctypes

# 基本的な型のエイリアス
BYTE  = ctypes.c_uint8
WORD  = ctypes.c_uint16
DWORD = ctypes.c_uint32

# セクタ情報
class NFD_SECT_ID(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("C",        BYTE),
        ("H",        BYTE),
        ("R",        BYTE),
        ("N",        BYTE),
        ("flMFM",    BYTE),
        ("flDDAM",   BYTE),
        ("byStatus", BYTE),
        ("byST0",    BYTE),
        ("byST1",    BYTE),
        ("byST2",    BYTE),
        ("byPDA",    BYTE),
        ("Reserve1", ctypes.c_char * 5),
    ]

# ヘッダ
class NFD_FILE_HEAD(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("szFileID",    ctypes.c_char * 15),
        ("Reserve1",    ctypes.c_char * 1),
        ("szComment",   ctypes.c_char * 0x100),
        ("dwHeadSize",  DWORD),
        ("flProtect",   BYTE),
        ("byHead",      BYTE),
        ("Reserve2",    ctypes.c_char * 10),
        ("si",          NFD_SECT_ID * (163 * 26)),
        ("Reserve3",    ctypes.c_char * 0x10),
    ]

if __name__ == "__main__":
    try:
        f = open(sys.argv[1], "br")
    except:
        print("usage: NFDdump <filename>")
        exit(1)

    header = NFD_FILE_HEAD()
    f.readinto(header)

    print(f"szFileID:{header.szFileID}")
    print(f"szComment:{header.szComment}")
    print(f"dwHeadSize:{header.dwHeadSize} (0x{header.dwHeadSize:X})")
    print(f"flProtect:{header.flProtect}")
    print(f"byHead:{header.byHead}")

    for i in range(len(header.si)):
        print(f"{i:4}:  ", end="")
        print(f"C:{header.si[i].C:02x} ", end="")
        print(f"H:{header.si[i].H:02x} ", end="")
        print(f"R:{header.si[i].R:02x} ", end="")
        print(f"N:{header.si[i].N:02x} ", end="")
        print(f"MFM:{header.si[i].flMFM} ", end="")
        print(f"DDAM:{header.si[i].flDDAM} ", end="")
        print(f"Status:{header.si[i].byStatus:02x} ", end="")
        print(f"ST0:{header.si[i].byST0:02x} ", end="")
        print(f"ST1:{header.si[i].byST1:02x} ", end="")
        print(f"ST2:{header.si[i].byST2:02x} ", end="")
        print(f"PDA:{header.si[i].byPDA:02x} ", end="")
        print(f"addr:{(header.dwHeadSize + i * 256):08x}")
#[EOF]
 

2026-02-25

NetBSD/i386 9.4でThunderbirdを動かそうとしたが断念したものの、Geminiとの対話は役に立った

NetBSD/i386 9.4のMATEデスクトップ環境が復活したので、Thunderbirdを入れてみようと思い立ちました。随分前にはpkgsrcでThunderbirdをインストールした記憶がありますが、何時の頃からかpkgsrcからは消えてしまいました。

 

NetBSDネイティブでなくても、LibreOfficeのようにLinuxエミュレーション環境でなんとかならないか試してみました。Geminiに相談しながら、インストールを試みます。インストール事態は、入手したファイルを展開するだけなので、別に難しくはありません。しかし実行しようとするとエラーが出ます。ここでGeminiに相談すると、いろいろとアドバイスしてくれます。

 

Geminiとの対話を繰り返しながら、あれこれやってみましたが、最終的にThunderbirdを動かすことはできないという結論に至りました。それはそれで構わないのですが、Geminiとの対話は、とても有効だったと感じました。Geminiのアドバイスが常に正確で間違いないという訳ではないのですが、それは現実の「詳しい知人」であっても似たようなものです。「詳しい知人」に比べてれば、Geminiの方が詳しい(知識量が多い)と思います。しかも「詳しい知人」は、機嫌を損ねると、へそを曲げたりしますが、Geminiには、そのような心配はありません。

 

Gemini(というか生成AI)は、うまく使えば、とても便利だと思います。しかし、Geminiが常に正しい情報を伝えてくれるわけでもなく、その伝えてくる情報を吟味して、何をするのか自分自身が理解している必要があると思います。Geminiがあれば、自分は勉強しなくても良いわけではなく、むしろ更に勉強しなければならないでしょう。

 

Geminiにも出来ることと出来ないことがあると思いますが、うまく使いこなせれば、とてもメリットがあると思います。 

T98-NEXTのNFD形式はxnp2では扱えなかった

NetBSD/i386でxnp2が動くようになり、PC-9801用Wizardryが動作したので、Windows 11上のT98-NEXTで保存したファイルを使おうとしたら、認識されませんでした。T98-NEXTではNFD形式ですが、xnp2はFDI形式を期待しているようです。FDイメージファイルの形式は、なんでも良いかと思っていましたが、そうでもないようです。

 

ネットを検索すると、FDイメージファイルの形式を変換するプログラムが見つかりました。NFD形式からFDI形式に変換できましたが、xnp2では認識されません。

 

変換されたフィルを確認すると、認識されるFDI形式とはファイルサイズが違うようです。強引ですが、バイナリエディタを使い、余分なところを切り詰めてみました。そうしたら、xnp2でも認識されるようになりました。

 

これで、Windows11上のT98-NEXTで途中までプレイした結果ファイル(NFD形式)を、FDI形式に変換し、末尾を切り落として、NetBSD/i386にコピーすれば、xnp2で続きをプレイできます。途中の手順がやや面倒ですが、機械的に処理する方法を探ってみようと思います。

2026-02-24

NetBSD/i386 9.4上のxnp2 0.86でPC-9801用Wizardryが動いた

NetBSD/i386 9.4にxnp2 0.86をインストールできたので、PC-9801用のWizardryを動かしてみました。何のトラブルに見舞われることもなく、あっさりと動作しました。

 

Windows11上でT98-NEXTを使っている場合には、FDイメージのファイルには拡張子「.NFD」がつくのですが、xnp2は「.FDI」か「.D88」を期待するようです。しかしこれは拡張子のネーミングの問題に過ぎないと思うので、FDイメージの構造が異なるわけではないでしょうから、おそらく問題にはならないと思います。

 

 xnp2は、FDイメージファイルをアクセスする際に、FDドライブのシーク音を出します。これはT98-NEXTには無かった機能です。物理的なFDドライブが存在するわけではないので、気分の問題ですが、シーク音を耳にすると懐かしさを覚えます。またリセット直後には、おなじみの「ピポッ」という音がなります。これはT98-NEXTもそうでした。これが聞こえるとPC-9801の思い出が蘇ります。

 

これで、Windows11上のT98-NEXTと、NetBSD/i386上のxnp2で、Wizardryが遊べるようになりました。両方の環境を使って遊ぶには、途中経過を記録したFDイメージの取り扱いが問題になってくるでしょう。何か良い方法を考えようと思います。 

NetBSD/i386 9.4でxnp2 0.86が動作した

NetBSD/i386 9.4環境で、パッケージからnxp2をインストールしようとしました。そうしたら共有ライブラリの不整合が発生し、pkgin full-upgradeを実行しました。するとエラーが出て、Geminiに相談しながら調査したら、パッケージ管理ディレクトリが/var/db/pkgと/usr/pkg/pkgdbの2箇所に見つかり、これが原因で深刻な不整合が起きていることが発覚しました。Geminiのアドバイスを受けながら、インストール済みの全パッケージを削除し、入れ直しました。しかし欠けているパッケージがあるので、確認しながら、追加インストールして、ようやくMATEデスクトップ環境が復活しました。

 

あらためて、本来の目的だったxnp2をインストールしました。何も問題がなくインストールできました。

 

動かしてみたのですが、PC-9801の起動時画面が出るはずですが、文字がかすれて読めません。フォントを別途入手する必要があるようです。Neko Project 21/Wのサイトから「font.bmp」を入手して「~/.np2/font.bmp」に置きました。これで起動時画面の文字が読めるようになり、一歩前進です。

 

次は、Wizardryが動くか確認しようと思います。 

2026-02-23

NetBSDのパッケージ管理情報が/usr/pkg/pkgdbと/var/db/pkgで混乱していた

NetBSD/i386にxnp2を入れようとしたら、共有ライブラリのトラブルが発生したので、pkginを使ってfull-upgradeを試みました。そうしたら、MATEが消えてしまい、ログインできなくなりました。あらためてpkginでMATEをインストールしようとしたら、多くのエラーメッセージが出て、インストールできません。

 

以前であれば、問題解決のためにGoogleで検索して似たような事例を探していたのですが、最近はGeminiに相談するようになりました。MATEのパッケージがインストールできないので解決したいというのが、Geminiに相談したことなのですが、Geminiがアドバイスしてくれたコマンドを叩くとエラーが発生します。それをGeminiに伝えると、また別のアドバイスをくれるので、対話を繰り返していると、次第に深みにはまってきました。

 

トラブルの根本原因は、NetBSDのパッケージ管理情報が/usr/pkg/pkgdbと/var/db/pkgの2か所に存在していることが判明しました。このように2か所に存在しているので、双方の情報が重複したりしていて、不整合も発生しているようです。こうなってしまうと、MATEがどうこうという問題よりも、この不整合を如何に解決するかという方が、重大な問題になりました。

 

Geminiのアドバイスを貰いながら対処を試みましたが、深刻な障害が解消しません。多少強引ですが、最後の奥の手として、インストール済みのパッケージをリストしておき、インストール済み全パッケージを削除し、あらためてインストールしなおすことにしました。

 

今は、インストール済のパッケージを削除しているところです。1,000以上ものパッケージがインストールされていたので、削除するだけでも長時間かかります。削除が終わったら、再インストールしますが、それも長時間かかるでしょう。 

NetBSDにバイナリパッケージxnp2を入れたらMATEが動かない

NetBSDのパッケージにはPC98エミュレータxnp2が含まれていることがわかったので、バイナリパッケージを入れてみました。バイナリパッケージを入れるのに「pkgin」を利用しています。OSがNetBSD/i386 9.0なので、パッケージ2025Q4のNetBSD/i386の9.0用を参照しました。

 

xnp2に依存関係のあるパッケージが有るのか無いのか不明ですが、最近1年以上もパッケージを更新していなかったので、追加更新に60個弱のパッケージが必要になりました。各々のパッケージが本当に必要なのか厳密に確かめることができるかもしれませんが、それをするなら「pkgin」を使う意味がないと思い、それらのパッケージを全てインストールしました。

 

インストール自体には問題がありませんでしたが、ログインできなくなりました。正確には、XDMでログインしたあとで、MATEを動かしているのですが、共有ライブラリが見つからないとのことで、XDMログイン画面に戻ってしまいます。rootは、MATEではなく,TWMのままなので、問題ありませんでした。「~/.xsession-errors」を確認すると、共有ライブラリが見つからないとあります。確かにその通りでしたが、シンボリックリンクを張れば解決できそうなので、対処しました。そして再度ログインしてもXDMに戻ってしまいます。別の共有ライブラリが見つからないようです。これを繰り返したら、最終的に共有ライブラリに定義されていないものがあるというエラーになりました。

 

もうお手上げなので、Geminiに相談したら、libxkbcommonという共有ライブラリを再インストールすることを勧められました。やってみましたが、解決しませんでした。それをGeminiに訴えると「pkgin full-upgrade」を試してみるように言われました。これなら解決できそうですが、作業時間が長いですし、xnp2とは関係ないところで、別な不具合が出てくるかもしれません。

 

しかし、これ以外に対処方法はないと思うので、もう後戻りはできない状態になっています。何か別の問題がでたら、また解決方法を模索するしかないでしょう。 

2026-02-21

NetBSDでPC98エミュレータ

Windows 10を使っていたころに、T98-NEXTというPC98エミュレータを使ってWizardryを遊んでいました。「PC-9800シリーズエミュレータの現況」という記事によると、PC98エミュレータは他にもあるようです。しかしMicrosoft Windowsなら選択肢は多いかもしれませんが、非Windowsでは選択肢が限られると思います。

 

ちょっと検索したら、次のような記事が見つかりました。これらの記事では、NP2kai(Neko Project II 0.86 kai)を使って、ソースからビルドしているようです。OSにはUbuntuを使っているようなのですが、私はNetBSD/i386を使いたいのです。

 

上述した記事を参考にすれば、NetBSD/i386環境であっても、ソースからビルドできるのではないかと思います。ビルド環境を整えたり、ビルドしてみてエラーが出るかもしれませんし、何かと手間がかかるだろうとは思います。もしかしたら何の苦労もなくビルドできてしまう可能性もありますが、そう簡単ではないだろうと予想しています。

 

自前でビルドする前に、ちょっとパッケージを確認したら、なんと「emulators/xnp2」がありました。これは「Xnp2 is a port for UNIX with X11 of "Neko Project II" PC-9801 emulator.」だそうです。自前でビルドしなくても、既にパッケージが用意されていました。

 

このパッケージをインストールしてみようと思います。このPC98エミュレータでWizardryを動かしてみようと考えていますが、問題なく無事に動いてくれるか心配です。もしトラブルようであれば、デバッグすることになるかもしれませんので、自前でビルドできる環境を整えておく必要が出てくるかもしれません。 

2026-02-18

モバイルSuicaを使ってみる

スマホにモバイルSuicaの設定を済ませたので、実際に利用してみました。何かトラブルが発生するのではないかと戦々恐々だったのですが、何事もなく、あっさりと利用できました。あまりにも何も問題がないので、驚いています(問題がなくて、あたりまえだとは思いますが)。

 

強いて言えば、スマホケースにストラップを付け、胸に下げているのですが、そのため改札を通る際に、多少腰をかがむ必要があるくらいでしょうか。


この調子なら、Smart ICOCAのクイックチャージのサービスが終了しても、モバイルSuicaに移行出来そうです。

2026-02-14

/var/spool/clientmqueue

FreeBSD/amd64を使用して自宅のメール環境を構築しています。fetchmailで取り込み、procmailで振分け、GNUSでメールを読んでいます。メールを送る時はmewを使うか、場合によってはMicrosoft Windows上のThunderbirdを使用しています。数日前、procmailを呼び出すのに、.forwardで指定するのを止めて、.fetchmailrcに変更したところでした。

 

procmailの呼び出し方法を変えただけなので、これまでと同様にメールが振り分けられるはずです。実際その通りなのですが、FreeBSD/amd64自身のrootが送信したメールが見当たらないことに気づきました。root宛のメールは、aliasesでローカルユーザに渡しています。

 

まず最初に確認するのはログファイルです。/var/log/maillogを除くと、エラーが記録されています。同じようなエラーが何度も出力されているようです。目視した限りでは、次のエラーでした。

  1. alias database /etc/mail/aliases.db out of date
  2. get_error=error:0A000126:SSL routines::unexpected eof while reading
  3. info=Bare carriage return (CR) not allowed

 

1番目のエラーは、Webを検索すると「FreeBSD: サーバーOS更新 12.3 → 12.4」という情報が見つかりました。newaliasesすれば良さそうです。

 

2番目と3番目は手強そうなので、Geminiに助けを求めました。Geminiは、「その問題なら、こうすれば解決しますよ」とか気軽に応答するのですが、なかなか解決に至りません。しかも提示された解決策を試みるとエラーが出るので、それを報告すると、「すいません、間違っていました。本当は、この方法です」と別な方法を示してきます。生成AIの回答らしいと言えば、そのとおりです。その分野のエキスパートに尋ねても、やっぱりこうなるかもしれません。生成AIなら、現実の人間と違って、何度訊き返しても嫌がったり怒り出したりする心配がないのは、メリットのような気がします。

 

Geminiとの対話を2時間ほど費やしたあげく、なんとか解決に至りました。いろいろ試したし、試行した内容を取り消したりもしたので、混乱しましたが、次のような対策を講じました。

  1. /etc/mail/ホスト名.submit.mcに「FEATURE(`msp', `[127.0.0.1]', `MSA', `E')」を追加した。
  2. /etc/mail/ホスト名.mcに「DAEMON_OPTIONS(`Name=IPv4, Family=inet, Modifiers=b')」と変更した。IPv6の方も同様に変更した。

 

このように変更してsendmailサービスを再起動すると、溜まっていたメールが流れる「はず」だったのですが、溜まったままでした。Geminiも半ばお手上げ状態で、スプールにあるメールを「tr -d '\r'」しながら、senmailに渡し、強制的に配送しました。これが成功したので、スプールにあるファイルは物理的に削除しました。

 

そもそも、何故このような事態に陥ってしまったのか、その原因は不明です。前回と今回の問題解決において、Gemini(というか生成AI)の威力を実感しました。それと同時に、間違い(勘違い?)を含む回答もしてくるので、Geminiの回答を鵜呑みにするわけにはいかないだろうと思います。そういう意味で、Geminiの指示に淡々と従うわけにはいかないでしょう。生成AIがあれば、何も勉強しなくても万々歳とはならないと思います。 

2026-02-12

BIMI-Indicator

自宅では、FreeBSD/amd64を使用してメール環境を構築しています。fetchmailで取り込み、procmailで振分け、GNUSで読んでいます。つい最近になって、ふと気づくと/var/spool/mqueueに配送できなかったメールが溜まっていることに気づきました。

 

溜まっているファイルを確認したら「SMTP error: 552 5.6.0 Headers too large (32768 max)」というエラーが記録されています。他のファイルも同様でした。このメッセージを手掛かりにWebを検索してみたのですが、解決に至る情報がみつかりませんでした。そこでGeminiに助けを求めてみました。

 

Geminiとの対話では、このメッセージを提示するところから始めました。これだけの情報では、一般論しか返ってこないので、対話を繰り返しながら、問題が解決できるような方向に話を進めました。解決策は幾つかあるようです。これまでは、上述したように、fetchmailで取り込み、.forward経由でprocmailを呼び出して振分けていました。.forwardを利用することでsendmailが介入することになりますが、問題となっているメールには「BIMI-Indicator」という巨大なヘッダがついていて、これが根本的な問題だろうと思います。

 

メッセージが指摘するように「Headers too large」なので、sendmailが「32768 max」という制限をかけているのを緩和するのも一つの方法です。他の方法として、fetchmailから直接procmailを呼び出すこともできて、そうすれば.forwardを使わないので、sendmailが介入することもありません。こちらの方法を採用することとして、.fetchmailrcに設定を追加しました。

 

対策を講じたことで、問題は解決したようです。

 

本来であれば、巨大な「BIMI-Indicator」というヘッダをつけている送信元が対処して欲しいところです。サイトのメールサーバの設定によっては、32kで制限をかけているところがあるでしょうから、そこでも問題が発生しているのではないかと思います。

2026-01-28

Windows11のメディアプレイヤーでCD情報が表示されないのはspecificationなのかundocumented featureなのか

2025年12月初め頃から、Windows11のメディアプレイヤーにおいてCD情報が表示されなくなっているという報告がありました。Microsoftからは公式アナウンスが出ていないようなので、偶々何かの調子が悪いだけなのか判断できませんでした。2026年に入ってからは、Web上に次のような情報があります。あいかわらずMicrosoftから公式発表がないので、憶測でしかありません。

 

なぜMicrosoftが公式に何も発表しないのかわかりませんが、そもそも「仕様」だったのだろうかという疑問をもちます。MicrosoftがWindowsを開発するにあたり、社内的には何らかの仕様書が存在する(はずだと思いますが、さすがに)のでしょうけれども、それが外部に伝わることはありません。私自身としては、Microsoft Windowsの一人のユーザとして、「Windows仕様書」というものは、これまでに見たことがありません。「ウィンドウズの使い方」のような文書なら見たことがありますが、それは「仕様書」というほどのものではなくて、「このような使い方がありますよ」という簡単な説明でしかありません。

 

メディアプレイヤーを使うとCD情報が表示されるということは、昔から気付いていましたが、それが「仕様に基づく」のか「偶々そうなっているだけ(実は何かのバグ?)」なのか、はっきりした説明を受けたことはありません。暗黙のうちに、CD情報が表示されるのが「仕様ではないか」と思い込んでいただけなのかもしれません。むしろ「undocumented feature」だったのかもしれません。

 

「仕様」だったのであれば、Microsoftに対して正式に説明を求めることができるとおもいます。しかし「undocumented feature」なのであれば、Microsoftに問い合わせても「そのような挙動が過去にあったとしても、メディアプレイヤーの機能の一部ではありません」と回答されて終わりになってしまうでしょう。

 

遥か昔にTK-85で8085を学んでいたころ、16x16のマトリクスで示されている命令一覧において、空白(未定義)となっているところにも、面白い動作をする「未定義命令」が隠してありました。しかし正式な命令として仕様書に記載されていたわけではありませんから、正式に問い合わせても(そんなことをした経験はありませんが)、正式に「知りません」と回答されるのがオチでしょう。

 

閑話休題。メディアプレイヤーが、今後もCD情報を表示しないのか、突然復旧するのか、皆目見当もつきません。僕個人の対策としては、Apple iTunesをインストールすることにしました。Windows10の頃には使用していましたが、Windows11に移行するにあたり不要かと考えていました。手持ちの音楽CDをメディアプレイヤーで再生できないわけではありませんが、CD情報が出ないのは、多少気になります。そのためだけにiTunesを利用するつもりです。 

 

 

2026-01-24

findコマンドのオプション「-newerXY」

U*IXでは、BSD系でもLinux系でも、階層的なディレクトリ構造を辿ってファイルを見つけるには、findコマンドを使うのが標準的です。もっともオプションの指定方法が独特なので、昔から使い慣れているオプション以外は、よく知りませんでした。それでも、今まで特に困ったこともなかったのですが、これまで使ったことのない便利なオプションが(何時の間にやら)追加されていたことを知りました。

コマンド実行時点を基準に、指定した日付よりも以前とか以後のファイルを見つけるには、オプション「-mtime」を使えばよいと思っていました。また指定されたファイルを基準に、それよりも新しいファイルを見つけるには、オプション「-newer」を使えばよいと思っていました。これだけでも不自由はないのですが、オプション「-newer」では基準となるファイルを用意しておく必要があるので、もっと便利な方法がないだろうかと思っていました。

 

Webを検索したら、オプション「-newerXY」というものがあるようです。これは何だろうと調べてみたら、いくつか記事が見つかりました。

 

このオプションは昔は無かったと思うので、いつの間にか追加されたのだと思います。これを使えば、オプション「-newer」のようにファイルを用意しておかなくても、特定の日時を基準にしてファイルを見つけられるようになります。素晴らしい。

 

findに限らず、よく利用しているコマンドにおいては、使い慣れたオプション以外に何があるのか改めて確認したりすることがありません。もしかすると便利なオプションが増えているかもしれないので、ちゃんとマニュアルを確認しておく必要があると思いました。

2026-01-15

NHKラジオに「Enjoy Simple English」という英語教育番組があり、数年前から聴いています。テキストも購入しています。2025年10月から2026年3月までの放送は、2021年度上半期の再放送です。

 

2025年1月15日放送は、「Marie the Scientist」というシリーズにおける「Why Do People Die?」という内容でした。この冒頭は、次のように始まります。

Narration (Marie): Dear Diary, Today, Ms. Sato, our homeroom teacher, didn't come to school. So Mr. Fujita taught our class. Haruto asked Mr. Fujita ...

Haruto: Do you know where Ms. Sato is?

Mr. Fujita: She had some bad news. Her mother died last night.  

 

この部分は、学級担任の佐藤先生の代わりに藤田先生が現れたので、ハルトが「佐藤先生は、どうしたんですか?」と聞いているところです。このような状況は、学校に限らず、何処ででもあると思います。

 

ただし、ここで気になるのが「Do you know where Ms. Sato is?」という英語表現です。直訳すれば「佐藤先生が何処に居るか、知っていますか?」となるでしょう。意訳すれば「どうして来ないんですか?」とかなるところでしょう。

 

以前にも紹介したことがありますが、Japan Timesの週刊ST(2012年4月6日号)の連載「English across Cultures」(青山学院大学名誉教授 本名信行)の中に、次のような記述があります。

一つの例を挙げましょう。ある会合に、出席する予定だった人が来ていなかったとします。後日、その人に尋ねるとき、日本人は「私はあの会合に行きましたが、あなたはどうして来なかったのですか」と言います。これを英語にすると "I went to the meeting. Why didn't you come?" となります。ところが、ネイティブスピーカーは、こういう状況では、"I was there. Where were you?" というような言い方をします。同じ状況を、ネイティブスピーカーは動きではなく、存在としてとらえるのです。

 

これはまさに、上述した会話と同じ状況です。日本語でも英語でも、発想は「何某は何故居ないのか?」であったとしても、言葉で表現すると、日英で異なってしまうようなのです。この事実は、日本人が英語を学ぶ際に引っかかるところだと思います。日本人の表現を英語に直訳しても、それが英語圏で通じないということはないでしょう。しかし英語圏の表現の仕方を踏まえて英語で表現すれば、よりスムースな会話になると思います。

 

英語を勉強する中で、このような日英の表現の違いというものは、気にしなければならない必須事項ではないと思いますが、注意を払っておく方が良いのではないかと、私自身は考えています。本件に限らず、日英の表現の違いがあると思いますので、英語に接する際には注意してみていこうと思います。

2026-01-13

JRE POINT IDとJRE IDは違う

JR西日本がSMART ICOCAのサービス終了をアナウンスしており、代替となるサービスを検討しています。いまのところ候補となっているのは、JR西日本の「モバイルICOCA」か、JR東日本の「モバイルSuica」です。どちらでも構わないのですが、まずはモバイルSuicaを試してみようと思います。

 

まず最初に、スマホが対応しているのかを確認しておきます。僕が使っているのは「Redmi Note 10T」です。これは「モバイルSuica・PASMO対応機種一覧掲載.pdf」に掲載されていますので、大丈夫です。

 

これまでは「おサイフケータイ」を使ったことがありませんでした。スマホにはアプリが入っているので、起動し、初期設定を済ませました。

 

次に「モバイルSuicaをはじめる」の手順に従い、作業します。最初にアプリをダウンロードし、それを起動します。ここで「JRE ID」へのログインが必要なのですが、気が付いてみれば些細なことだったのに、大きく躓いてしまいました。既にIDを持っているならログインするのですが、ログインしようとすると「IDかパスワードが間違っている」というエラーになります。そんなハズはないと思い、パソコン側でログインしてみましたが、問題なくログインできます。しかしスマホではログインできません。入力ミスなのかと、目を皿のようにして確認しましたが、入力を間違えていないようです。

 

スマホの画面には「ログインできない場合」の情報がリンクされていました。それを辿ってみたら、「JRE IDはJRE POINT IDとは違います」という情報があり、ここで目が覚めました。僕が入力していたのは、「JRE ID」ではなく「JRE POINT ID」だったのです。

 

「JRE ID」を新規作成してもよいのですが、2026年2月には「JRE POINT」サイトでも「JRE ID」が利用可能になるようです。それまで待ってから、続きを作業しようかと考えています。 

2026-01-10

PDF24 Toolsでしおりを一括定義するためのスクリプトを試作

PDF24 Toolsを使うと、JSON形式のファイルを読み込ませることで、しおりを一括定義できることを知りました。JSON形式なので、普通のエディタで編集できるのです。しかし、しおりとして必要な情報以外に、JSON形式とするために必要な構造としなければなりません。簡単なスクリプトを組んで、JSON形式ファイルを生成してみました。ただし試作版なので、最低限のことしかできません。誰もが使えるツールという訳にはいきませんが、作業の省力化には役立ちそうです。

 

 #!/bin/sh

genJSON () {
    echo "["
    while IFS=, read x y; do
        echo "  {"
        printf '    "title":"%s",\n'     "$y"
        printf '    "dest":[%s,"Fit"]\n' "$x"
        echo "  },"
    done
    echo "]"
}


cat <<*EOF* | genJSON
3,Table of Contents
5,SPECIFICATIONS
8,MODULE FUNCTIONS
*EOF*
#[EOF]

PDFの「しおり」を一括登録したい

PDFのページに与えられている「しおり」を編集する方法を模索しています。Windows11にはPDFgear 2.1.12というアプリを入れてあり、しおり編集機能があります。しかし、既存のしおりを「一括削除」できないようなので、一つずつ削除するのは大変です。何とかならないかと思って調べてみました。

 

PDF24 Toolsというオンラインで完結するツール群があるようです。この中にある「PDFのブックマークを編集」を使えば、一括削除できそうです。

 

これで一件落着と思ったのですが、しおりをJSONファイルに保存したり、指定したJSONファイルに従ってしおりを定義したりすることができるようでした。このJSONファイルの形式が、PDFのしおり編集として一般的に使われているのか、PDF24 Tools独自なのかはわかりません。しかしJSON形式は、普通のエディタで編集できますので、手作業でしおりを追加していくよりも便利ではないかと思います。

 

意外なきっかけで、面白いツールに巡り合いました。 

TW5のdaysオペレータの挙動を類推する

TW5のdaysオペレータの挙動を、記事「Days Operator and Examples (documentation addition)」や「Filter tiddlers by date: today and in the past」を熟読したり、TW5上で実際の動作を確認したりして、類推してみました。

  • デフォルトではフィールド「modified」を対象とするが、サフィックスで任意のフィールドを指定できる。
  • 日付は、「Date Fields」に記されているように、「YYYYMMDDHHMMSSCCC」という17桁の数字列で表現される。
  • 日時は、ローカルタイムではなく、UTCとして扱われる。
  • days[N]は、N日後以前のTiddlerを得る。
  • !days[N]は、N日後以降のTiddlerを得る。
  • days[-N]は、N日前以降のTiddlerを得る。
  • !days[-N]は、N日前以前のTiddlerを得る。
  • days[0]は、今日だけのTiddlerを得る。
  • !days[0]は、今日以外のTiddlerを得る。

 

このような挙動だとすると、今日以降の一週間以内のTiddlerを得るには「!days[1]days[6]」のように指定するのではないかと思います。ただし境界となる日を含むか否かを考えれば、Nの指定は別の値になるかもしれません。

 

daysオペレータは手強いと思いますが、なんとかなりそうな感触が得られたので、TWCからTW5に以降する作業を進められそうです。 

手荒れとしての汗疹や霜焼けが改善

10年ほど前には、手荒れが酷く、夏は汗疹、冬は霜焼けに苦しめられていました。あまりにも症状が酷いので皮膚科の病院に行ってみたりしました。通院していたのは短期間でしたが、それ以降、毎日薬用ハンドクリームを塗るように心掛けてきました。

 

すぐに効果がでたとは思いませんし、数年前までは、汗疹にも霜焼けにも苦しめられ続きました。

 

しかし昨年夏には酷い汗疹にはなりませんでした。また今冬は霜焼けになっていません。ようやく手荒れが直ってきたような気がします。 

feedlyがダウンしている

RSSフィードを利用するためfeedlyを利用しています。GoogleがRSSサービスを廃止した時に乗り換えました。基本的な動作に不満はないのですが、たまにサービスが利用できなくなるのが困りものです。2026年1月10日現在、サービスがダウンしているようです。Webを検索すると、同様の障害が報告されています。

 

しばらくすれば復旧すると思いますが、すぐに直るのか、しばらくかかるのか不明です。このような事態が、頻発しているとは言いませんが、全くないわけではなく、一年に何度かあります。

 

feedly以外にもRSSサービスがあるので、別のサービスに乗り換えた方が良いだろうかと思うこともあります。しかし別のサービスが安定しているとも限りません。別のサービスを使うことが問題解決になるとは考えられません。ともかく復旧を待とうと思います。 

2026-01-09

SMART ICOCAのサービスが終了する

JR西日本が「20年間、ありがとう。次のスマートは、モバイルICOCAで。」という発表をしています。2026年10月31日をもってSMART ICOCAによるクイックチャージを終了するそうです。私自身はSMART ICOCAを持っていて、クレジットカードでチャージしています。今年11月からはクイックチャージ出来なくなるので、何か代替方法を探す必要があります。

 

JR西日本としては「モバイルICOCA」に移行して欲しいようです。それが最も正統なのだと思いますが、他の交通系ICカードを選択することもできると考えています。全国各地の交通系ICカードを何か持っていれば、個々にサービスの違いはありますが、基本的に鉄道運賃の支払いは可能ですから、ICOCAに拘らず、KITAKAだろうが、SUGOKAだろうが、何でも構わないと思います。ただし僕自身の条件としては、JRが指定するクレジットカードではなく、任意のクレジットカードでチャージできることです。それが、今までならSMART ICOCAでした。

 

僕の希望を満たすのは、「モバイルICOCA」か「モバイルSuica」が選択肢となりそうです。どちらを選択しても良いと思いますが、少なくともスマホにアプリを入れる必要があります。さらにスマホで「おサイフケータイ」を使う必要があります。これまで「おサイフケータイ」を利用したことがないので、ちょっと心配です。

TW5のdaysオペレータは手ごわい

これまでTiddly Wiki ClassicReminder Pluginを入れてToDoリストのようなものを構築していました。今はTW5を利用しているので、TWCからTW5に移行しようと考えています。ところがプラグインのTW5版が見つからないので、同等機能をTW5で実現する必要があります。

 

実現方法の詳細は分からないのですが、daysオペレータが使えるのではないかと当たりをつけ、その挙動を調べてみました。しかし手ごわいです。僕の使用目的が、このオペレータの本来の目的とは違うのだろうと思います。僕としては、ある日を基準として、それから1週間以内とか、1か月から3か月以内などの期限日を持つTiddlerを一覧したいのです。

 

このオペレータの挙動が良く分からないので、Webを検索して見つけた情報の中から「Filter tiddlers by date: today and in the past」という情報が参照されていました。この情報は2016年頃なので、もう10年ほど前のものです。この中に次のようなことが書かれていました。

last 5 days:
<<list-links '[days:due[-5]!days:due[-1]sort[due]]'>>
next 5 days:
<<list-links '[!days:due[1]days:due[5]sort[due]]'>>


daysオペレータは、このように利用すればよいのかと思いましたが、手ごわいとも感じました。それはともかくとして、これで何とかなりそうです。

2026-01-07

TWCからTW5に移行するために必要なReminder Plugin代替策

普段からTiddlyWikiを活用しています。使い始めたころは、今で言うところのTWC(TiddlyWiki Classic)でした。現在はTW5を主に使用しており、TWCの頃のTiddlyWikiはTW5に置き換えるようにしています。基本的にはTWCの情報をTW5に移すだけなのですが、ひとつだけ「TiddlyWiki Reminder Plugin」を利用しているものがあり、移行できずにいます。

 

TW5にも同等のプラグインがあれば良いのですが、今のところ見つけられていません。もっともプラグインの機能を全て使いこなしているわけではないので、同等機能をTW5で実現するだけで構わないと考えています。必要な機能としては、特定の日付を期限とする事柄をTiddlerとして用意しておいて、その1週間前とか1か月前などのタイミングで該当するTiddlerを列挙したいのです。ToDoリストのようなものです。

 

実現方法を考えているところですが、「days Operator」を利用すればよいのではないかと思っています。試しに使ってみましたが、挙動がよくわかりません。TW5の機能を実現しているJavaScriptは「$:/core/modules/filters/days.js」のようです。処理は短いものの、TW5の内部構造などに精通していないと、ロジックを理解できません。

 

情報を求めてWebを検索したところ、「Days Operator and Examples (documentation addition)」という2018年9月7日付の投稿を見つけました。もう10年弱ほど前の情報ですから、現状では変わっているところがあるかもしれません。しかし他に何か別の適切な情報のあてがあるわけでもないので、これを読むところから始めたいと考えています。