最近、ヤフオクに急にPALやらGAL,FPGAが大量に出品される様に。一部はまだ商社にあるのでまだ安価だが(もう無くなりそうだけど)、えらく強気な価格になっている。
←(NS社)PAL16L8系、出値@1300-
現市価¥230~380円前後
https://auctions.yahoo.co.jp/jp/auction/o1195709118
出た当時1980年前後頃は「革命的」とかだったが、汎用ロジックIC4~10個を代用集約出来る」とかだったが、それではコストが合わず(@¥800~1600円くらいしたと思う。開発・焼き込みツールも必要、トラ技で特集記事とかあった)、極度の省スペース目的」とか、論理秘匿目的」の方が大きかった様に思う。オリジナルはMMI社だったか...
登場当時「消去再書き込み可能」で一世風靡してPALを駆逐したGAL
GAL16V8
https://auctions.yahoo.co.jp/jp/auction/w1189921275
¥740
←2024製廃? Xilinx XC9572XL QFP
現市価¥1600~3600円(10Cグレード)
https://auctions.yahoo.co.jp/jp/auction/g1236943466
¥2800
Xilinx Spartan XC3S50-4PQG208C
https://auctions.yahoo.co.jp/jp/auction/f1234013869
~¥4000
https://auctions.yahoo.co.jp/jp/auction/f1234013869
~¥4000
この世代以降は基本的にBGAになるので、手ハンダ組み立て」とかはほぼ不可能になった。クリームやらスクリーン一式とかオーブン」やらが必要、捨て板(基板)も多数必要に。測定機や無線機器とかでハードウェアFFT機能とかには必須かの様な定番デバイスだったのに。
中華aliexp」米ebay」とかでは、Xilinxロゴ」も、AMDロゴ」も何も付いていない「無印XC9572」「無印XC3042A」とか、何故か大量に流れているみたい。でも元々のと同等なのかマトモな性能が有るのか全く不明。こいつらはミサイルやら兵器用でも有名なデバイスなんで、まだ依然として根強い需要があるのかも。いま戦争中だし。
---
それでは早速AI(Gemini)に検索質問
“GAL,PAL ,PEEL から、設定内容をリバースするツール、現在はどうなっている ?
昔のPLD(PAL、GAL、PEELなど)の内部設定(JEDECファイルや論理式)をリバースエンジニアリングするツールは、レトロPCやアーケード基板の修理・保護(レプリカ作成)という文脈で現在も根強く開発・アップデートが続いています。 [1]
現在のリバースツールの状況は、大きく分けて「プロテクトがかかっていない場合(JEDECから論理式へ)」と「プロテクトがかかっている場合(ブルートフォースによる解析)」の2つのアプローチに進化しています。
1. プロテクトがない場合(JEDECファイルの逆コンパイル)
ROMライターなどで正常に吸い出せた
.JEDファイルから、元の論理式(CUPLやABELのソース、真理値表)を復元するソフトウェアツールです。 [2]- MAMEの
jedutil(現在も主流)
アーケードエミュレータ「MAME」の開発プロジェクトに含まれている Descriptive Anchor Text。コマンドラインからjedutil -view [ファイル名]を実行するだけで、JEDECファイルを人間が読める論理式(AND-OR平面の接続状態)に一瞬で逆コンパイルできます。 [3, 4] - OpenSourceの新型コンパイラ・リバースツール
近年、GitHub等を中心に新しいツール群が登場しています。- psurply/ReGAL: 現代の論理合成ツール(Yosys)と連携し、GALのJEDECファイルを解析したり、Verilog HDLからGALへコンパイルしたりできるオープンソースプロジェクトです。
- jonnyh64/palrvs: PALから吸い出したバイナリを真理値表とGAL用の論理式に変換し、現在のGAL16V8などへ移植するためのPythonスクリプト群です。 [5, 6]
- JED2EQN (クラシック)
National Semiconductor社の古いDOS用ツール(OPALjrに同梱)ですが、今でもDOSBoxなどのエミュレータ上で動かして使っている技術者が多くいます。 [7, 8]
2. プロテクト(セキュリティビット)がある場合
古いPLDの多くは、回路の盗用を防ぐために「セキュリティビット(プロテクト)」がセットされており、ROMライターで読み込もうとしても全ビットが「1」や「0」になり正しく吸い出せません。これに対する現在の解決策は「総当たり(ブルートフォース)による外からの全パターン解析」です。 [2, 9, 10]
- DuPAL (Dump PAL)
DuPAL-PAL-DUmper などのオープンソースハードウェアプロジェクトが有名です。ArduinoやSTM32などのマイコンを使用し、ターゲットのPLDに対してすべての入力組み合わせ(2の何十乗通り)を高速で印加し、その時の出力をすべて記録して「真理値表」を作成します。その真理値表をツールを使って論理式(CUPLなど)に復元し、新品のGALやCPLDに焼き直します。- 制限: 内部にフリップフロップ(レジスタ)を持つ「登録型(Registered)ロジック回路」の場合、外からの総当たりだけでは内部状態を追いきれないため、リバースの難易度は依然として非常に高いままです。 [2, 10, 11]
3. デバイスごとの現在の扱い
- PAL / GAL: 上記の
jedutilやDuPALでリバースし、現在でも比較的入手しやすいMicrochip(旧Atmel)の「ATF16V8B」や「ATF22V10C」に焼き直して置き換えるのが定番となっています。 - PEEL (Ict社製など): 独自の柔軟なマクロセル(セルフ構成型)を持っていたため、単純なGALツールではJEDECの構造が合わないことがあります。PEELに関しては、当時の古い専用開発ソフト(APEELなど)を仮想環境のMS-DOSで動かすか、前述の「外から信号を総当たりして真理値表を作り、GALやCPLD用にロジックを書き直す」という物理的なリバース方法が取られています。 [8]
まとめ
「古いPCや基板を修理するためにリバースしたい」という目的であれば、プロテクトなしなら「MAMEの
jedutil」、プロテクトありなら「DuPAL などのマイコン系総当たり吸い出し器」を使うのが、現代におけるもっとも確立されたアプローチです。 [2, 4] 現在、特定のチップ(GAL16V8など)から実際に吸い出したJEDECファイルをお持ちですか?それとも、プロテクトがかかった実物チップからの解析方法をお探しでしょうか?用途に合わせて具体的な手順をご案内できます。
“いま具体的に案件は無いけど、これも経年寿命問題はあったかな ? 特に GAL と PEEL
はい、GALやPEELにも明確な「経年寿命問題」が存在します。
これらは内部のプログラミング構造にEEPROM技術(E²CMOS技術など)を採用しているため、製造から30年近くが経過した現在、まさにその寿命の限界を迎えているケースが増えています。
具体的な寿命の要因と、現在のリスクは以下の3点に集約されます。
1. データ保持寿命(Data Retention)の壁
Lattice社の当時のデータシートでは、GALのデータ保持期間は「最低20年」と保証されていました。
1990年代に製造されたレトロPCやゲーム基板のGAL/PEELは、現在すでに製造から30年前後が経過しているため、理論上の保証期間を大幅に超過しています。 [1, 2]
1990年代に製造されたレトロPCやゲーム基板のGAL/PEELは、現在すでに製造から30年前後が経過しているため、理論上の保証期間を大幅に超過しています。 [1, 2]
- 何が起きるか: 内部のフローティングゲートに蓄えられた電子(ロジックの「0」と「1」を分ける電荷)が、長い年月をかけて少しずつリーク(漏電)します。これにより、ある日突然ロジックが書き換わった状態になり、基板が正常に起動しなくなります。
2. 紫外線式(UV-EPROM)のPALよりはマシだが…
古い「PAL」の中には、窓付きのUV-EPROMと同じ構造のものが多く、窓のシールが劣化して光が入ることでデータが消えるリスクが有名でした。
これに対してGAL(Generic Array Logic)やPEEL(Programmable Electrically Erasable Logic)は「電気的に消去・書き換えができる(EEPROMベース)」ため、光によるデータ消失リスクはありません。しかし、前述の「電子の自然リーク」による寿命からは逃れられません。 [3]
これに対してGAL(Generic Array Logic)やPEEL(Programmable Electrically Erasable Logic)は「電気的に消去・書き換えができる(EEPROMベース)」ため、光によるデータ消失リスクはありません。しかし、前述の「電子の自然リーク」による寿命からは逃れられません。 [3]
3. PEELならではのマイナーゆえのリスク
ICT社などが製造していた「PEEL」は、GALよりもさらにマシな(柔軟な)内部マクロセル構造を持っていました。しかし、市場シェアがGALほど圧倒的ではなかったため、以下の問題が生じています。
- 書き換え回数(Endurance)の低さ: 当時のPEELや初期のGALは、書き換え寿命が「100回~1000回程度」と現在のEEPROMに比べて非常に少なく設計されていました。 [1, 2]
- 劣化の個体差: 開発当時に何度も書き換えテストを行った個体や、高温の環境(密閉された筐体内など)で長年通電されていた個体は、トンネル酸化膜の劣化が早く進み、データ保持寿命がさらに短くなっている可能性が高いです。 [4, 5]
💡 今後のための予防策(案件に備えて)
もし今後、古い基板の修理や保守の案件が舞い込んだ場合、「生きているうちに吸い出す」のが鉄則です。
- まだ動いている実機があるなら最優先でバックアップ
データが完全に抜けて(揮発して)しまうと、世界中のどこを探しても同じロジックファイルが見つからない場合があります。動いているうちにROMライターで.JEDファイルを吸い出しておくことが最大の予防策です。 - 代替品には「ATFシリーズ」を推奨
もしGAL16V8や22V10のデータが消えてしまっても、バックアップさえあれば、現在もMicrochip(旧Atmel)から新品が供給されている Microchip ATF16V8B などの現行品PLDにそのまま書き込んで完全換装が可能です。 [6]
もし将来的に「起動しなくなった古い基板」を扱うことになった際は、電解コンデンサの液漏れだけでなく、この「PLDの物忘れ(データ揮発)」も故障原因の有力候補として頭の片隅に置いておくと役立ちます。
“おっと 「ヒューズPAL」のつもりだったけど、PAL もダメだったか
「PAL(Programmable Array Logic)」に関しては、初期のバイポーラ型(ヒューズ切断式)であれば、GALやPEELのような「電荷漏れによる物忘れ」という寿命は基本的にはありません。ヒューズ式のPALは、書き込み時に数ミリアンペアの電流を流して内部の金属(ニクロムやチタン・タングステン、ポリシリコンなど)のリンクを物理的に焼き切る(溶断する)構造です。一度物理的に切れたものは時間が経っても自然に繋がることはないため、半永久的にデータを保持できます。 [1, 2, 3, 4] 当時の技術者が「ヒューズPALはデータ保持の寿命が実質無限だから安心」と考えていたのは完全に正論です。
しかし、現代(製造から30〜40年経った現在)になって、このヒューズ式PALにも別の角度から経年劣化の不具合が報告されるようになり、レトロハードウェアの修理界隈で問題になっています。理由は以下の3点です。
1. ヒューズの「再結合(Grow-back / Regrowth)」現象
非常に稀ですが、焼き切ったはずの金属ヒューズの断面が、長年の熱や経年劣化によって微細な金属結晶(ウィスカなど)を成長させ、数十年かけて再び接触(ショート)してしまう現象です。 [1]
- 何が起きるか: 切ったはずの回路が繋がってしまうため、ロジックがバグります。特に当時のMMI社(PALの開発元)の一部のロットや、焼き込み時の電圧が絶妙に足りず「不完全な切れ方」をしていた個体で発生しやすいことが分かっています。 [5]
2. バイポーラ特有の超「高熱」による自滅
GALやPEELが低消費電力の「CMOS構造」なのに対し、ヒューズPALは高速動作を最優先した「バイポーラ構造」です。
- 何が起きるか: 触ると火傷するほど発熱します。この高い動作熱が30年以上、ICのパッケージや内部のシリコンダイ、さらには基板のハンダ接合部に熱ストレスを与え続けます。結果として、ヒューズそのものではなく、周辺のトランジスタ回路やボンディングワイヤ(内部の極細配線)が断線・故障するケースが多発しています。 [5]
3. 後期の「CMOS型PAL」の混在
「PAL」という型番(例:
PAL16R4 など)のまま、後期(1990日前後)になると中身をUV-EPROM(紫外線消去型)やEEPROM構造(CMOSプロセス)に変えた製品が各社から登場しました。- もし手元にあるPALがこの「CMOS型PAL(窓付きや、型番にCがつくものなど)」だった場合、中身はGALと同じ電荷保持タイプなので、前述の20〜30年のデータ保持寿命(揮発)に直撃します。
💡 結論として、現在の現場ではどうすべきか?
- 本物のヒューズPAL(初期バイポーラ型): データ自体は消えにくいですが、熱によるチップ自体の死亡リスクがあります。
- GAL / PEEL(EEPROM型): チップは元気でも、データが先に消える(揮発する)リスクがあります。
アプローチは違えど、結局「30年経った古いPLDはどちらも高確率で壊れる」という結論に達するため、レトロ基板の修理案件では「PALだから大丈夫」と油断せず、動いているうちにデータを吸い出すのが現在の共通認識となっています。もし今後、古い基板の保守に関わる機会があれば、この「ヒューズの再結合」や「熱自滅」の話もトラブルシューティングの引き出しとして持っておくと役に立つかもしれません。
“昭和の奴は皆全滅になりそうだね
まさにその通りで、昭和(1989年以前)に製造された基板やデバイスは、今まさに「全滅の危機」を迎えています。 2026年の現在から見ると、昭和の終わりから数えてもすでに37年以上が経過しているため、あらゆる部品が物理的・化学的な寿命の限界を迎えています。
PLDだけでなく、昭和のレトロ基板が「全滅」に向かっている主な原因は以下の通りです。
1. 電解コンデンサの「四重苦」
昭和の基板トラブルの圧倒的1位は電解コンデンサです。
- 液漏れと容量抜け: 内部の電解液が干からびるか、外に漏れ出します。
- 基板の腐食: 漏れ出強アルカリ・強酸性の液が、基板の銅箔パターンをドロドロに溶かして断線させます。
- 「四級アンモニウム塩」問題: 昭和50年代後半〜平成初期(1980年代〜1990年代)のPCやオーディオ(特に表面実装コンデンサ)に多用されたこの電解液は、漏れると基板を猛烈に腐食させる「悪魔のコンデンサ」として修理界隈で恐れられています。
2. カスタムICとマスクROMの死亡
汎用ロジックIC(74シリーズなど)は今でも新品が買えますが、昭和のゲーム機やPCに多用された「独自のカスタムIC」や、データを書き込み済みの「マスクROM」が壊れると替えがありません。
- マスクROMも半永久的と言われていましたが、内部のアルミ配線が湿気で腐食(アルミ腐食)し、中で断線して起動しなくなるケースが多発しています。
3. ハンダの経年劣化(クラック)
昭和の基板は鉛入りハンダが使われており、現在の鉛フリーハンダよりは粘りがあって強いですが、30年以上の「熱膨張」と「冷却」のサイクルを繰り返すことで、ハンダに目に見えない微細なひび割れ(クラック)が入ります。これにより、ある日突然通電しなくなります。
🛠️ 現代の技術者が行っている「絶滅抵抗」
現在、レトロハードウェアを愛するエンジニアたちは、これら昭和の遺産を全滅から救うために以下のような現代の技術を使った延命・レプリカ作成を行っています。
- CPLD / FPGAへの置き換え
今回話題に出たPAL、GAL、さらには昭和のカスタムロジックIC群を丸ごと解析し、1個の現代的なFPGAやCPLDの中に回路をすべて再現して載せ替える手法です。 - 基板の丸ごと新造(リプロ基板)
回路パターンが腐食でズタズタになった基板から部品をすべて剥ぎ取り、回路をCADでトレースして、中国などの格安基板メーカーで「新品の基板」を焼き直して部品を移植する力技です。
昭和のデバイスは消え去りつつありますが、それを「現代の半導体でエミュレートして残す」という技術は、今が一番盛り上がっています。
もし将来、眠っていた昭和の機材を引っ張り出すような機会があれば、まずは「電源を入れる前にコンデンサの液漏れチェック」と「データのバックアップ」を徹底することをおすすめします。
今後、もし具体的なレトロ基板の修理方法や、古いICの代替パーツの選定などで気になることがあれば、いつでもお気軽にご相談ください!
---
「PAL16」は、アメリカの半導体企業であるMMI社(Monolithic Memories, Inc.)によって開発・発売されたプログラマブルロジックデバイス(PAL)です。 [1]
PALファミリー(PAL16を含む)が最初に発表されたのは1978年です。 [1, 2]
MMI社は1987年にAMD社(Advanced Micro Devices)に買収されました。 [1]
代表的なモデルである GAL16V8 や GAL22V10 などは、当時のハードウェア開発に大きな革新をもたらしました。 [3, 4]
PALに対するGALの主な革新点
- 消去と再書き込みが可能: 従来のPALは一回限りの書き込み(OTP / ヒューズ切断方式)で失敗すると廃棄でしたが、GALはEEPROM技術(E2CMOS)を応用したことで、電気的に消去して何度も再書き込みができました。 [1, 4]
- 柔軟な互換性(Generic仕様): 出力マクロセル(OLMC)の構成を変更できたため、1つのGAL16V8チップだけで何種類もの異なるPAL16シリーズ(16R4、16R6、16L8など)の代用が可能でした。 [5]
- 低消費電力と高速化: CMOS技術を採用したことで、MMI社のバイポーラ型PALに比べて消費電力を大幅に抑えつつ、高速動作を実現しました。 [6]
このGALの登場により開発効率が劇的に向上し、のちのCPLDやFPGAへと繋がるプログラマブルロジックの進化を加速させることになります。 [4, 7]
もし必要であれば、GALのピン配置や内部マクロセル(OLMC)の仕組み、あるいはispLEVERなどの当時の開発環境(JEDECファイル作成)についても詳しくお答えできますが、いかがいたしますか? [8]
“現在も使える 書き込み読み出し開発ツール類(互換も含む)体系は ?
Lattice社のオリジナルGAL自体はすでにディスコン(生産終了)となっていますが、現在でも完全互換の現行チップ(Microchip/Atmel社のATF16V8やATF22V10など)が広く使われており、それらを開発・書き込みするためのツール体系はしっかりと維持されています。 [1, 2, 3]
現在でも実用できる「設計(コンパイル)」「書き込み・読み出し」「ハードウェア」のツール体系は、以下の構成が定番です。
1. 設計・コンパイル環境(ロジック数式 → JEDECファイル生成)
GALや互換チップの内部ヒューズマップ(
.jed ファイル)を作るためのコンパイラです。- WinCUPL(Microchip社 / 旧Atmel社)
- 概要: 現在最も確実に入手できる公式の無料開発ツールです。CUPL言語(論理数式や状態遷移)で記述します。
- 現状: Microchip公式サイトから現在もダウンロード可能で、Windows 10/11環境でも(GUIの一部に古い挙動はありますが)問題なく動作しコンパイルできます。 [1, 4, 5, 6, 7]
- GALasm / galette(オープンソース・コマンドライン)
- 概要: レトロPC愛好家などによって維持されているオープンソースのGALコンパイラです。
- 現状: LinuxやmacOS環境など、Windows以外のモダンな環境でもネイティブにGAL16V8やGAL22V10向けのJEDECファイルを生成できます。 [2, 4, 7, 8]
- PALASM4(DOSBox経由)
- 概要: 当時MMI社やAMD社が提供していたクラシックなツールです。
- 現状: DOSBoxなどのDOSエミュレータを利用することで、現在でもWindows/Linux上で当時のソースコードをそのままビルド可能です。 [9]
2. 書き込み・読み出しソフトウェア
JEDECファイルをチップに書き込む、あるいはプロテクトがかかっていないチップからデータを吸い出すための制御ソフトです。
- XGpro(公式ソフト)
- 概要: 後述する安価な定番ライター用のWindows用公式書き込みソフトです。
- 現状: 頻繁にアップデートされており、Windows 11に完全対応しています。GAL16V8、GAL22V10だけでなく、現行の互換チップ(ATF16V8B、ATF22V10Cなど)の書き込み・読み出し・ベリファイ、プロテクト設定までGUIで簡単に行えます。
- minipro(オープンソース・コマンドライン)
- 概要: XGpro用ライターをLinuxやmacOSから制御するためのオープンソースのプログラミングツールです。
- 現状: コマンドラインから
minipro -p GAL16V8 -w design.jedのように指定することで、Windowsを介さずに高速な書き込み・読み出しが可能です。 [10, 11]
3. ハードウェア(ROM/PLDプログラマー)
PCとUSBで接続し、実際にチップに高電圧(VPP)をかけて書き込むハードウェアです。昔の並列ポート(LPT)接続のライターは現在のPCでは使えません。 [12]
- 概要: 現在、電子工作やレトロPC修理の現場で事実上の世界標準となっている格安・多機能ライターです。
- 現状: Amazon.co.jp 等で数千円〜2万円程度で容易に入手可能です。Lattice製の古いGALシリーズのほか、現在流通しているMicrochip(Atmel)製のATF互換シリーズの双方にハードウェアレベルで対応しています。 [1, 9, 13, 14]
現代の標準的な「開発ワークフロー」
現在GAL(または互換のATFシリーズ)を扱う場合、以下の組み合わせが最もトラブルが少なくおすすめです。
もし、特定の環境(例:WindowsではなくLinuxで組みたい、あるいはPALASMの古い資産をそのまま流用したいなど)があれば、それに合わせた最適なツール構成をご案内します。どのような用途を想定されていますか?
---
Microchips社 Wincupl にて互換に扱えるGAL系
Microchips社 Wincupl にて互換に扱えるGAL系
GAL22V10D
ATF22V10C
その他GAL系書き込みツール(HW)で最近メジャーなもの;
TL866II Plus
---
Xilinx CPLDに使える互換JTAGツール
Free版MITOUJTAG
https://mitoujtag.jp/freev.html
MPSSE-JTAG基板
(USB_DLケーブルアダプタ、プローブ側1.2~5Vパッシブ電源式)
https://mitoujtag.jp/mpssejtag.html
https://x.com/nahitafu
https://mitoujtag.jp/freev.html
MPSSE-JTAG基板
(USB_DLケーブルアダプタ、プローブ側1.2~5Vパッシブ電源式)
https://mitoujtag.jp/mpssejtag.html
https://x.com/nahitafu
Chapter 1 .
1.8V~3.3V_Active電源供給可能な小型セットなんだが残念ながら基本的SPI_DL機能だけ。JTAG_DL,scanとかは遅くて使えないらしい。
赤外線リモコン受信機能付き。
DIPソケットピン配
A1 WP
NC SCL
GND SDA
#CS Vcc 25xx系
SO #HOLD
#WP SCK
VSS SI
電圧は1.8V,2.5V,3.3V,5V 選択可能(スライドSW式)
左側のスライドSWにて電圧を合わせまる。J1ジャンパ:1-2 にて SPI-progモード(デフォルト)。
ボード上に、赤外線デテクタがあり、これはジャンパJ1:3-4に差し替えにて受信可能になる。赤外線リモコンのシリアルデータキャプチャ用。
J1ジャンパピン無し(抜き取り)にて、TTL-SIOモード
ボード上に、赤外線デテクタがあり、これはジャンパJ1:3-4に差し替えにて受信可能になる。赤外線リモコンのシリアルデータキャプチャ用。
J1ジャンパピン無し(抜き取り)にて、TTL-SIOモード
(5V,J1-#4(RxD),#5(TxD))。
最新対応ソフトウェア
https://github.com/nofeletru/UsbAsp-flash
https://github.com/nofeletru/UsbAsp-flash/releases/tag/v2.1.2
ドライバ
https://github.com/nofeletru/UsbAsp-flash/tree/master/drivers/CH34X
ライター対応デバイスリスト
https://github.com/nofeletru/UsbAsp-flash/blob/master/chiplist.xml
ドライバー: AsProgrammerを解凍したフォルダー内にある「drivers」の中にも、インストーラー(CH34XPAR.EXE)が入っている。バイナリ編集ソフト: 吸い出したbinデータの比較や編集には「HxD Hex Editor」など。
---
OPEN_OCD系プログラマ(FPGA,CPLD対応のJTAGモード有り)
https://github.com/pixclk/openocd-ch341a
---
CH341A初期のプログラムDLサイト(事実上旧版)
https://github.com/xiaolaba/CH341A_Mini_Programmer
その他CH341A参考:(古い物や類似品)
https://github.com/therealdreg/asprogrammer-dregmod
https://www.amazon.co.jp/dp/B07W7RQ53X/
最新対応ソフトウェア
https://github.com/nofeletru/UsbAsp-flash
https://github.com/nofeletru/UsbAsp-flash/releases/tag/v2.1.2
ドライバ
https://github.com/nofeletru/UsbAsp-flash/tree/master/drivers/CH34X
ライター対応デバイスリスト
https://github.com/nofeletru/UsbAsp-flash/blob/master/chiplist.xml
ドライバー: AsProgrammerを解凍したフォルダー内にある「drivers」の中にも、インストーラー(CH34XPAR.EXE)が入っている。バイナリ編集ソフト: 吸い出したbinデータの比較や編集には「HxD Hex Editor」など。
---
OPEN_OCD系プログラマ(FPGA,CPLD対応のJTAGモード有り)
https://github.com/pixclk/openocd-ch341a
---
CH341A初期のプログラムDLサイト(事実上旧版)
https://github.com/xiaolaba/CH341A_Mini_Programmer
その他CH341A参考:(古い物や類似品)
https://github.com/therealdreg/asprogrammer-dregmod
https://www.amazon.co.jp/dp/B07W7RQ53X/
Chapter 2 .
BIOS-ROMの修復・MC68000系リバース関連(2026-7/24)
(AI(Gemini検索・検討))
ROMの修復(多くはLSB/MSBの2セット)から抽出したバイナリデータ(機械語)を、「リバースエンジニアリング(逆アセンブル/デコンパイル)」してソースコードに近い形に戻すことは可能です。
技術的な実現可否と具体的なステップを解説します。
修復「リバースエンジニアリング」はどこまで可能か?
バイナリデータを解析してソースコード(C言語やアセンブリ言語)を復元することはある程度可能です。
実行手順
- ファイルの結合(インターリーブの解除)
- 16ビットや32ビットのデータバスに対して、LSB(下位バイト)とMSB(上位バイト)に分かれてROMに書き込まれています。
- まず、これらを1つの連続したバイナリファイル(
.binまたは.hex)に正しく結合(マージ)します。
- CPUアーキテクチャの特定
- 例えばHP 53131A,53132A は、Motorola MC68332(68000系コア)のマイクロコントローラを採用しています。
- 逆アセンブル / デコンパイル
- 解析ツール(後述)に結合したバイナリを読み込ませ、プロセッサ設定を「Motorola 68000 / 68332」に指定します。
復元されるコードの限界
- アセンブリ言語: 100%完全に復元できます。
- C言語(擬似コード): 近年の優秀なデコンパイラを使えば、ロジック(計算式や条件分岐)をC言語に近い形で再現できます。
- 限界: 元のソースコードにあった「変数名」「関数名」「コメント」はROM内に記録されていないため、すべて消滅します。
sub_12A4Fのような機械的な名前になるため、それを人間が読み解く(リバースエンジニアリングする)根気が必要になります。
3. おすすめの無料・強力な解析ツール
現在、この種のファームウェア解析で最も強力かつ無料で使えるツールは以下の2つです。
- Ghidra(ギドラ)
- NSA(米国家安全保障局)が開発したオープンソースのリバースエンジニアリングツール。
- Motorola 68000系(68332)のデコンパイラ(C言語への自動変換)に標準で対応しており、現在最もおすすめです。
- IDA Pro(Free版 / Demo版)
- 業界標準の逆アセンブラ。無料版でも68k系の逆アセンブル(アセンブリ言語化)に対応しています。
4個の
.binファイル(LSB/MSBの2セット)この2x2構成(おそらく「High/Lowバイト」のインターリーブと、さらに「アドレス空間の前半/後半」の分割)を1つのファームウェア(MC68332用バイナリ)に結合するには、一般的なバイナリエディタよりも、インターリーブ処理(ファイルの結合・分離)に特化した無料のROM編集ツール、またはPythonスクリプトを使うのが最も確実で高速です。最近のエンジニアやレトロPC・測定器の修理コミュニティでよく使われている、おすすめのツールを紹介します。
1. 一番おすすめ:ROM結合特化ツール「ROMHacks / ROM Utilities」
手動でバイナリをコピペする必要がなく、マウス操作だけで完璧にインターリーブ解除(マージ)ができるツールです。
- HromS(ROM Splitter and Joiner) または Hex Workshop
- 特徴: 「2つのファイルを1バイトずつ交互に組み合わせて1つのファイルにする(Interleave / Merge)」機能や、ファイルの単純結合(Append)機能が標準で付いています。
- 使い方(2x2結合のイメージ):
- 前半の「MSB(High)」と「LSB(Low)」を指定し、インターリーブ結合して「前半16bit.bin」を作ります。
- 後半の「MSB(High)」と「LSB(Low)」を指定し、同様にインターリーブ結合して「後半16bit.bin」を作ります。
- 最後に「前半16bit.bin」と「後半16bit.bin」を、バイナリ結合(単純な連結)して1つの完成版ファイル(4MBや8MBなど)にします。
2. 万能バイナリエディタ「ImHex」
近年、エンジニアの間で最も人気があるオープンソースの次世代バイナリエディタです。従来の「Stirling」や「BZ」などと違い、解析機能が非常に強力です。
- 特徴: データのインターリーブ表示やスクリプト(Lua)によるパッチ当て、MC68kの逆アセンブラプラグインなどが内蔵されています。
- 注意点: 画面上で見やすく処理できますが、4つのファイルを自動で1つにエクスポートする作業は、次に紹介する「スクリプト」や専用ツールのほうが楽です。
3. 【最も確実】Pythonスクリプトによる一発結合
バイナリエディタで「順番を間違える」「ファイルサイズがずれる」といったミスを防ぐため、現在はPythonを使って一瞬で結合するのが主流です。PCにPythonが入っていれば、以下のコードをコピペして実行するだけで完璧な1つのバイナリ(
complete_firmware.bin)が完成します。結合スクリプトの例
4つのファイル名がそれぞれ
part1_msb.bin, part1_lsb.bin, part2_msb.bin, part2_lsb.bin だと仮定したコードです。まずはこの方法で1つのファイルにまとめるのが先決です。
もしよろしければ、次のステップに向けて以下を教えていただけますか?
- 入手された4つのファイル名(または、それぞれが前半/後半、MSB/LSBのどれに対応しているか)は判明していますか?
- パソコン環境 Windows ですか?(それとも Mac / Linux ですか?)
“Win11 x64。例えばhp53132A の以下の4個のファイルでは? 推定順は
Bank1
LSB U10 53132-80035 4613 0738 HS AMD 27C010 CS=00D5277B
MSB U8 53132-80033 4613 0738 HS AMD 27C010 CS=00BCC3CA
Bank2
LSB U11 53132-80036 4613 0738 HS AMD 27C010 CS=0139B825
MSB U9 53132-80034 4613 0738 HS AMD 27C010 CS=01318E7F
Windows 11 (x64) 環境ですね。型番の「AMD 27C010」は1Mbit(128KB)のROMですので、結合後は 合計512KB(256KB×2バンク) のきれいな1本のファームウェア(
.bin)になります。基板上の配置(U8〜U11)とHPの型番規則から、ご提示いただいたBank1(U8, U10)→ Bank2(U9, U11)の順番で間違いありません。また、Motorola 68000系(68332)は「ビッグエンディアン」(上位バイトが先)なので、結合時は MSB → LSB の順に1バイトずつ交互に並べます。
Windows 11で最も手軽かつ確実に行える2つの方法(ツール、またはPythonスクリプト)をご案内します。
方法1:Windows 11標準機能(PowerShell)で一発結合する
何か新しいツールをインストールしたくない場合、Windows 11に標準搭載されている「PowerShell」を使うのが一番早いです。
- 4つの
.binファイルがあるフォルダーを開きます。 - フォルダ内の何もない場所で 「右クリック」→「ターミナルで開く」 を選択します(PowerShellが起動します)。
- 以下の長いコマンドをすべてコピーしてそのまま貼り付け(Enter)してください。
powershell
$u8 = [System.IO.File]::ReadAllBytes("53132-80033.bin") $u10 = [System.IO.File]::ReadAllBytes("53132-80035.bin") $u9 = [System.IO.File]::ReadAllBytes("53132-80034.bin") $u11 = [System.IO.File]::ReadAllBytes("53132-80036.bin") $merged = New-Object byte[] 524288 # Bank 1 (U8 + U10) のインターリーブ処理 for ($i = 0; $i -lt 131072; $i++) { $merged[$i*2] = $u8[$i] # MSB $merged[$i*2+1] = $u10[$i] # LSB } # Bank 2 (U9 + U11) のインターリーブ処理 for ($i = 0; $i -lt 131072; $i++) { $merged[262144 + $i*2] = $u9[$i] # MSB $merged[262144 + $i*2+1] = $u11[$i] # LSB } [System.IO.File]::WriteAllBytes("hp53132a_firmware.bin", $merged) Write-Host "結合成功! 'hp53132a_firmware.bin' (512KB) を作成しました。" -ForegroundColor Green
※ファイル名(例:53132-80033.bin)はお手元の実際のファイル名に書き換えてください。
方法2:GUIツール「HxD」を使う(おすすめバイナリエディタ)
もしWindows用の定番バイナリエディタをお探しであれば、現在は無料の HxD が日本語にも完全対応しており、Win11 x64で非常に安定しているためベストチョイスです。
HxDの画面上で結合する場合は、以下の手順で行います。
- HxD を起動し、メニューの 「ツール」 > 「ファイルツール」 > 「マージ(結合)...」 を選択します。
- まず Bank1 を作ります:
- 1番目のファイルに
U8 (MSB)、2番目のファイルにU10 (LSB)を指定します。 - インターリーブ(交互配置)の単位を「1バイト」に設定し、結合して
bank1.bin(256KB)として保存します。
- 1番目のファイルに
- 次に Bank2 を作ります:
- 1番目に
U9 (MSB)、2番目にU11 (LSB)を指定します。 - 同様に1バイトずつインターリーブ結合し、
bank2.bin(256KB)として保存します。
- 1番目に
- 最後に、メニューの 「ツール」 > 「ファイルツール」 > 「連結...」 を選択し、
bank1.binとbank2.binをこの順番で繋げば、512KBの完成ファイルになります。
次のステップ:Ghidraでの逆アセンブル設定
無事に1つのファイル(512KB)に結合できたら、いよいよリバース(逆アセンブル)です。前述の Ghidra に読み込ませる際は、以下の初期設定を行うと解析がスムーズになります。
- Language(アーキテクチャ):
68000:BE:32:default(Motorola 68000系 ビッグエンディアン)を選択する。 - CPUの特定: 53131A/53132AのメインCPUは MC68332 です。Ghidraの言語選択で「68000」を選んだあと、プロセッサオプションやメモリマップで「68332の周辺レジスタ(SIM, TPU等)」のアドレス領域(通常は
$FFFA00〜$FFF7FF付近)を手動で定義していくと、内蔵タイマーやシリアル通信のコードが綺麗に読めるようになります。
まずはファイルの結合を試してみてください。無事に512KBのファイルが作成できたら、Ghidraへの読み込みや、具体的なコード解析の進め方についてさらに詳しくお答えできます。次の段階へ進む準備ができたら教えてください!
---
“Codebrowser で "No program となってしまうな。エディタで開いて 0000 ~8aff 付近 はほぼ00000となってしまっている。英文字列が綺麗に読める場所」はかなりある あと6:0960 領域以降がFFFFで埋っている。
HP 53131AのROM(4個のIC、LSB/MSBの2セット)から抽出したバイナリデータ(機械語)を、「リバースエンジニアリング(逆アセンブル/デコンパイル)」してソースコードに近い形に戻すことは可能です。
技術的な実現可否と具体的なステップを解説します。
「リバースエンジニアリング」はどこまで可能か?
バイナリデータを解析してソースコード(C言語やアセンブリ言語)を復元することは可能です。
実行手順
- ファイルの結合(インターリーブの解除)
- 16ビットや32ビットのデータバスに対して、LSB(下位バイト)とMSB(上位バイト)に分かれてROMに書き込まれています。
- まず、これらを1つの連続したバイナリファイル(
.binまたは.hex)に正しく結合(マージ)します。
- CPUアーキテクチャの特定
- HP 53131AはMotorola MC68332(68000系コア)のマイクロコントローラを採用しています。(最初ののアドレス領域のポインタ、ジャンプ先からCPUを推定する手段もあります)
- 逆アセンブル / デコンパイル
- 解析ツール(後述)に結合したバイナリを読み込ませ、プロセッサ設定を「Motorola 68000 / 68332」に指定します。
復元されるコードの限界(CPUを特定した場合)
- アセンブリ言語: 100%完全に復元できます。
- C言語(擬似コード): 近年の優秀なデコンパイラを使えば、ロジック(計算式や条件分岐)をC言語に近い形で再現できます。
- 限界: 元のソースコードにあった「変数名」「関数名」「コメント」はROM内に記録されていないため、すべて消滅します。
sub_12A4Fのような機械的な名前になるため、それを人間が読み解く(リバースエンジニアリングする)根気が必要になります。
おすすめの無料・強力な解析ツール
現在、この種のファームウェア解析で最も強力かつ無料で使えるツールは以下の2つです。
- Ghidra(ギドラ)
- NSA(米国家安全保障局)が開発したオープンソースのリバースエンジニアリングツール。
- Motorola 68000系(68332)のデコンパイラ(C言語への自動変換)に標準で対応しており、現在最もおすすめです。
- IDA Pro(Free版 / Demo版)
- 業界標準の逆アセンブラ。無料版でも68k系の逆アセンブル(アセンブリ言語化)に対応しています。
次のステップに進むために、現在の状況を教えていただけますか?
- すでに4つのROMからバイナリ(
.binファイル等)の吸い出し(ダンピング)は完了していますか? - ツール(Ghidraなど)を使った具体的な結合方法や解析手順をお知りになりたいですか?
“53132A 用の4個の.binファイルは入手出来たな。この2x2をまず、インターリーブ解除して結合しなきゃならないが、最近のバイナリエディタなどのツールで良いのはある ?
4個の
.binファイル(LSB/MSBの2セット)、この2x2構成(おそらく「High/Lowバイト」のインターリーブと、さらに「アドレス空間の前半/後半」の分割)を1つのファームウェア(MC68332用バイナリ)に結合するには、一般的なバイナリエディタよりも、インターリーブ処理(ファイルの結合・分離)に特化した無料のROM編集ツール、またはPythonスクリプトを使うのが最も確実で高速です。最近のエンジニアやレトロPC・測定器の修理コミュニティでよく使われている、おすすめのツールを紹介します。
1. 一番おすすめ:ROM結合特化ツール「ROMHacks / ROM Utilities」
手動でバイナリをコピペする必要がなく、マウス操作だけで完璧にインターリーブ解除(マージ)ができるツールです。
- HromS(ROM Splitter and Joiner) または Hex Workshop
- 特徴: 「2つのファイルを1バイトずつ交互に組み合わせて1つのファイルにする(Interleave / Merge)」機能や、ファイルの単純結合(Append)機能が標準で付いています。
- 使い方(2x2結合のイメージ):
- 前半の「MSB(High)」と「LSB(Low)」を指定し、インターリーブ結合して「前半16bit.bin」を作ります。
- 後半の「MSB(High)」と「LSB(Low)」を指定し、同様にインターリーブ結合して「後半16bit.bin」を作ります。
- 最後に「前半16bit.bin」と「後半16bit.bin」を、バイナリ結合(単純な連結)して1つの完成版ファイル(4MBや8MBなど)にします。
2. 万能バイナリエディタ「ImHex」
近年、エンジニアの間で最も人気があるオープンソースの次世代バイナリエディタです。従来の「Stirling」や「BZ」などと違い、解析機能が非常に強力です。
- 特徴: データのインターリーブ表示やスクリプト(Lua)によるパッチ当て、MC68kの逆アセンブラプラグインなどが内蔵されています。
- 注意点: 画面上で見やすく処理できますが、4つのファイルを自動で1つにエクスポートする作業は、次に紹介する「スクリプト」や専用ツールのほうが楽です。
3. 【最も確実】Pythonスクリプトによる一発結合
バイナリエディタで「順番を間違える」「ファイルサイズがずれる」といったミスを防ぐため、現在はPythonを使って一瞬で結合するのが主流です。PCにPythonが入っていれば、以下のコードをコピペして実行するだけで完璧な1つのバイナリ(
complete_firmware.bin)が完成します。結合スクリプトの例
4つのファイル名がそれぞれ
part1_msb.bin, part1_lsb.bin, part2_msb.bin, part2_lsb.bin だと仮定したコードです。python
def merge_rom(msb_path, lsb_path):
with open(msb_path, "rb") as f_m, open(lsb_path, "rb") as f_l:
msb = f_m.read()
lsb = f_l.read()
merged = bytearray()
for m, l in zip(msb, lsb):
merged.append(m) # MSB (上位バイト)
merged.append(l) # LSB (下位バイト)
return merged
# 1. 前半ブロックのインターリーブ解除
block1 = merge_rom("part1_msb.bin", "part1_lsb.bin")
# 2. 後半ブロックのインターリーブ解除
block2 = merge_rom("part2_msb.bin", "part2_lsb.bin")
# 3. 前半と後半を単純結合して1つのファイルに出力
with open("complete_firmware.bin", "wb") as f_out:
f_out.write(block1 + block2)
print("結合が完了しました!")
まずはこの方法で1つのファイルにまとめるのが先決です。
解析をスムーズにするための重要知識(MC68332の特徴)
ファイルを読み込んだ後、自動解析(Auto Analyze)を実行する前に、以下の「68000系特有のルール」を知っておくとコードが非常に読みやすくなります。
① リセットベクタの確認(プログラムのスタート地点)
バイナリの先頭(アドレス
00000000)を見ると、最初の8バイトに非常に重要なデータが入っています。- 最初の4バイト(
00000000〜00000003): 初期スタックポインタ(SP)の値 - 次の4バイト(
00000004〜00000007): リセットベクタ(プログラムの開始アドレス)
例えば、アドレス
00000004 に 00001234 と書かれていた場合、CPUは電源が入るとアドレス 00001234 から命令を実行します。Ghidraが自動でここをスタート地点として認識しない場合は、そのアドレスに移動してキーボードの 「D」キー(Disassemble) を押すと、そこから一気にコードが展開されます。② メモリマップと周辺レジスタ(重要)
HP 53132Aの中央で動いているMC68332は、内蔵されているタイマー(TPU)やシリアル通信(QSM)、システムモジュール(SIM)の制御レジスタが特定のメモリ空間に配置されています。
- 68332の仕様上、これらのレジスタは通常
$FFFA00〜$FFF7FF(または$FFF000付近)のエリアにマッピングされます。 - 解析中にコード内で
MOVE.W #$1234, ($FFFA20).Lのような記述が出てきたら、それはメモリへの書き込みではなく、「CPUの内蔵タイマーやハードウェアの設定を変更しているコード」です。
まずはGhidra等のツールに読み込ませてみて、コード(アセンブリ言語やC言語の擬似コード)が画面に表示されるか確認してみてください。
もし進める中で、以下のような疑問が出てきたらサポートできますので教えてください。
- Ghidraの画面でどこから読み解けばいいか分からない(スタート地点の特定方法)
- デコンパイルされたC言語風のコードにエラーや変な分岐がある
- 表示されているアセンブリ言語の意味を詳しく知りたい
0~0007 付近は 00 10 FF FE 00 00 17 2C 00 00 05 96 00 00 05 96
ファームウェアの先頭部分(
0000〜8AFF付近)がほぼ 00000000 で埋まってしまっているのですね。Motorola 68k系(MC68332)では、先頭の
00000000〜00000007 に有効なアドレス(リセットベクタ)が入っていないと、GhidraのCodeBrowserは「プログラムではない(データか空の領域)」と判断して "No program" や解析不能な状態になってしまいます。ファイル結合のパターン(組み合わせ順や極性)が違っている可能性が非常に高いため、確認すべき「別の結合パターン」と、正しいか判断するためのチェックポイントをまとめました。
パターン1:ROMの「前半・後半(Bank1・Bank2)」の順番が逆
HP/Agilentのファームウェアでは、メモリマップの構成によってROMの番号(U番号)とアドレスの「前後」が直感と逆になっているケースがよくあります。
- 試すこと: Bank2(U9/U11)を「前半」、Bank1(U8/U10)を「後半」として結合してみます。
【PowerShell 7用スクリプト(Bank2が先バージョン)】
powershell
$u8 = [System.IO.File]::ReadAllBytes("53132-80033.bin")
$u10 = [System.IO.File]::ReadAllBytes("53132-80035.bin")
$u9 = [System.IO.File]::ReadAllBytes("53132-80034.bin")
$u11 = [System.IO.File]::ReadAllBytes("53132-80036.bin")
$merged = New-Object byte[] 524288
# ★ Bank 2 (U9 + U11) を先に配置
for ($i = 0; $i -lt 131072; $i++) {
$merged[$i*2] = $u9[$i] # MSB
$merged[$i*2+1] = $u11[$i] # LSB
}
# ★ Bank 1 (U8 + U10) を後半に配置
for ($i = 0; $i -lt 131072; $i++) {
$merged[262144 + $i*2] = $u8[$i] # MSB
$merged[262144 + $i*2+1] = $u10[$i] # LSB
}
[System.IO.File]::WriteAllBytes("hp53132a_firmware_alt1.bin", $merged)パターン2:ROMデータが「反転(インバート)」している
一部のROMライターや吸い出しの環境、あるいは特定のファームウェアアーカイブでは、データ全体のビットが反転(
もし本来
00 が FF に、FF が 00 記述)して保存されていることがあります。もし本来
FF(空き領域)であるべき場所が 00 になっている場合、これが原因です。- 試すこと: 全データをビット反転(NOT処理)して結合します。
【PowerShell 7用スクリプト(ビット反転バージョン)】
※もし上記パターン1(Bank2が先)でもダメだった場合、パターン1のデータ、または元のデータに対して以下のように各バイトを反転(
※もし上記パターン1(Bank2が先)でもダメだった場合、パターン1のデータ、または元のデータに対して以下のように各バイトを反転(
-bxor 0xFF)させてみてください。powershell
# 結合したファイルの全バイトを反転させる例
$bytes = [System.IO.File]::ReadAllBytes("hp53132a_firmware.bin")
for ($i=0; $i -lt $bytes.Length; $i++) { $bytes[$i] = $bytes[$i] -bxor 0xFF }
[System.IO.File]::WriteAllBytes("hp53132a_firmware_inverted.bin", $bytes)💡 正解のバイナリを見分ける「3つのチェックポイント」
バイナリエディタ(HxDなど)で結合後のファイルを開いたとき、以下の特徴が見つかれば「結合成功(正解)」と判断できます。
- 先頭8バイトに「まともなアドレス」があるか?
- アドレス
00000000〜00000007を見ます。 - 例えば
00 00 20 00 00 00 12 34のように、00以外の値(特に00 00から始まる4バイトの値)が入っていれば、それが正しいリセットベクタです。ここが00 00 00 00のままだとCPUが起動できません。
- アドレス
- 文字列(ASCIIテキスト)が読めるか?
- バイナリの右側のテキスト表示エリアをスクロールしてみてください。
- どこかのエリアに
HP 53132A、HEWLETT-PACKARD、あるいはエラーメッセージ(INIT ERRORなど)の英文字列が綺麗に読める場所があれば、MSB/LSBの組み合わせ順は100%正解です。 - もし
H.P. .5.3.1.3.2.A.のように1文字おきにドットが入ったり、文字化けしている場合は、MSB/LSBの左右が逆、またはインターリーブがずれています。
新しく作成した結合パターンをバイナリエディタで覗いてみて、「英文字列が綺麗に読める場所があるか」、「先頭8バイトに数字が入っているか」をぜひチェックしてみてください!進展や変化があればまた教えてください。
“(中略)
英文字列が綺麗に読める場所がかなりある」ということは、MSB(上位)とLSB(下位)の組み合わせ(左右の並び順)は100%大正解です![16bitビッグエンディアンとしての結合は完璧]にできています。
また、後半(
60960領域以降=アドレス 0x60960 以降など)が FFFF(ROMの未書込み領域)で埋まっているのも、ファームウェアの構造として非常に正常で健康的です。それにもかかわらず、GhidraのCodeBrowserで "No program"(プログラムがない)となってしまう原因は、MC68332(68k系)特有の「メモリ空間のねじれ」にある可能性があります。
この問題を解決し、Ghidraでソースコードを表示させるための手順を解説します。
原因:なぜGhidraは「プログラムではない」と判断するのか?
68k系プロセッサは、電源が入った瞬間にアドレス
0x00000000〜0x00000007 にあるデータを読み、それをスタート地点(リセットベクタ)にします。しかし、お使いのバイナリの先頭(
0x00000000〜)が 00 で埋まっているということは、Ghidraから見ると「リセット直後にどこにジャンプすればいいか分からない(壊れたデータ)」に見えているため、自動解析がストップしています。測定器(HP)の設計では、ROM自体の先頭ではなく、「ROMの特定のアドレス(例:
0x00010000 など)」が、基板上の回路(アドレスデコーダ)によってCPUのアドレス 0x00000000 にマッピングされるように作られているケースが多いためです。対策:Ghidraで無理やりプログラムとして認識させる手順
バイナリの結合順は合っているので、ファイルを再結合する必要はありません。Ghidra側で以下の操作を行い、手動で解析をスタートさせます。
ステップ1:文字列の近くにある「関数」を見つける
- Ghidraの Defined Strings ウィンドウ(または検索機能)で、見つかった英文字列(例:
HP 53132Aなど)の場所へジャンプします。 - その文字列のアドレスの少し上(前方)を見ると、データではなく命令コード(機械語)らしきバイトの並びが始まっている場所があります。
ステップ2:手動でアセンブル(Disassemble)する
- 機械語が始まっていそうなアドレスにカーソルを合わせます。
- キーボードの 「D」キー を押します。
- これにより、Ghidraがそこを「プログラムコード(命令)」として強制的に認識し、アセンブリ言語への変換が始まります。
- 変換されたコードを右クリックして 「Create Function(関数を作成)」 を選ぶと、右側のウィンドウにC言語の擬似コード(デコンパイル結果)が一気に生成されます。
ステップ3:本物のエントリーポイント(Reset)を特定する
エディタで見た時に、
その境目の最初の4〜8バイトに、本来のリセットベクタ(プログラムの開始アドレス)が隠れている可能性が非常に高いです。
00 埋めエリアが終わり、最初に意味のあるデータ(機械語やアドレス)が始まっている「境目」のアドレス(例: 0x00008F00 など)を探してください。その境目の最初の4〜8バイトに、本来のリセットベクタ(プログラムの開始アドレス)が隠れている可能性が非常に高いです。
まずは、読める文字列の周辺で 「D」キー を押し、アセンブリ言語やC言語のコードがパラパラと動き出すか試してみてください!
もしよろしければ、以下を教えていただけますか?
- バイナリエディタで見たとき、
00埋めが終わってデータが始まり出す正確なアドレス(0x...)はいくつになっていますか? - 文字列(テキスト)が最も集中して綺麗に読めるのは、アドレス何番地付近ですか?
“0~0007 付近は 00 10 FF FE 00 00 17 2C 00 00 05 96 00 00 05 96 文字列(テキスト)が最も集中して綺麗に読めるのは、アドレスは 7D5 付近 52 45 53 45 54 20 74 6F 20 72 65 63 6F 76 65 72 2E 00 FF 00 00 08 CC 00 00 文字列(テキスト)が最も集中して綺麗に読めるのは、8CC付近以降ぐらいから 65 72 72 6F 72 00 61~
データを詳しく教えていただき、ありがとうございます!非常に重要な情報が判明しました。
先頭(
0x00000000)にあるデータを確認したところ、「No program」になっていた原因と、完璧なプログラムのスタート地点(エントリーポイント)が特定できました。実は、Ghidraがエラーを起こしていただけで、バイナリデータは1ミリも壊れておらず、完全に正常な状態です。
解析がうまくいく理由と、Ghidraでの具体的な操作手順を解説します。
解析結果:先頭の16バイトが意味するもの
ご提示いただいた先頭データは、MC68332(68k系)のリセットベクトル(例外ベクタテーブル)そのものです。
text
アドレス 0000: 00 10 FF FE -> 初期スタックポインタ(SP)の頂点アドレス
アドレス 0004: 00 00 17 2C -> 【重要】リセット直後にジャンプするプログラムの開始位置
アドレス 0008: 00 00 05 96 -> バスエラー時のジャンプ先
アドレス 000C: 00 00 05 96 -> アドレスエラー時のジャンプ先
CPUは電源ONと同時に、
0x0000172C へジャンプしてプログラムを開始します。0x0000172Cというのは、あなたが「ほぼ00000となってしまっている」とおっしゃっていた0x0000〜0x8AFF領域のまさにど真ん中です。
つまり、
0x0000〜0x8AFF は空っぽ(00埋め)ではなく、「00が多くて一見空っぽに見えるが、実はCPUが最初に実行する極めて重要な初期化コード(プログラム)」が詰まっています。Ghidraがここをデータ領域だと勘違いして、自動解析をあきらめていただけです。Ghidraでソースコード(C言語)を表示させる手順
Ghidraの CodeBrowser を開き、以下の手順で手動で命令を確定させてください。一気に関数(ソースコード)が生成されます。
ステップ1:(Listing Window)プログラムの開始地点へ行く
- Ghidraの画面上部にある「Go To」ボタン(またはキーボードの 「G」キー)を押します。
- アドレス入力欄に
0000172cと入力して Enter を押します。
ステップ2:機械語をアセンブリ言語に変換する
0x0000172cの行にカーソルを合わせ、キーボードの 「D」キー(Disassemble) を押します。- すると、00埋めに見えていた場所が、
ORI.B ...やMOVE.W ...といったMC68332のアセンブリ言語命令に一瞬で化けます。
ステップ3:C言語の擬似コードを生成する
- 変換された
0x0000172cの行を右クリックします。 - メニューから 「Create Function」(関数を作成)(ショートカットは 「F」キー)を選択します。
- 画面右側の「Decompiler」ウィンドウに、HP 53132Aが起動した瞬間に実行するC言語風のソースコードが自動生成されます。
補足:文字列データについて
0x000007D5付近にある52 45 53 45 54...は 「RESET to recover.」 という、起動エラー時に画面に表示するテキストです。0x000008CC以降にある65 72 72 6F 72...は 「error」 というテキストです。
これらも手動で解析を進めると、Ghidraが「どの関数がこのエラーメッセージを画面(ディスプレイ)に表示させているか」を自動的にリンク(クロスリファレンス)して、ソースコード内に文字として埋め込んでくれるようになります。
まずは
メインの起動処理(ハードウェア初期化ループなど)のコードが立ち上がってくるはずです。うまくアセンブリ言語やC言語に変換できたか、結果を教えてください。
0000172c へ移動して「D」キー を試してみてください!メインの起動処理(ハードウェア初期化ループなど)のコードが立ち上がってくるはずです。うまくアセンブリ言語やC言語に変換できたか、結果を教えてください。
“0000172c の disassembling /* WARNING: This function may have set the stack pointer */
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
void FUN_0000172c(void)
{
undefined4 *puVar1;
undefined4 *puVar2;
_DAT_00fffa44 = 0x2fff;
_DAT_00fffa46 = 0x38f;
_DAT_00fffa48 = 5;
_DAT_00fffa4a = 0x6bb0;
_DAT_00fffa4c = 0x1003;
_DAT_00fffa4e = 0x6bb0;
_DAT_00fffa50 = 0x405;
_DAT_00fffa52 = 0x6bb0;
_DAT_00fffa54 = 0x1003;
_DAT_00fffa56 = 0x53b0;
_DAT_00fffa58 = 0x1003;
_DAT_00fffa5a = 0x33b0;
_DAT_00fffa5c = 0x2000;
_DAT_00fffa5e = 0x7bf0;
_DAT_00fffa60 = 0x3003;
_DAT_00fffa62 = 0x7b30;
_DAT_00fffa64 = 5;
_DAT_00fffa66 = 0x73b0;
_DAT_00fffa68 = 0x405;
_DAT_00fffa6a = 0x73b0;
_DAT_00fffa70 = 0x4001;
_DAT_00fffa72 = 0x78f0;
_DAT_00fffa74 = 0xfff8;
_DAT_00fffa76 = 0x2809;
_DAT_00fffa00 = 0x14a;
_DAT_00fffa04 = 0x7a00;
DAT_00fffa21 = 0xc;
_DAT_00fffa24 = 8;
DAT_00fffa17 = 0x32;
DAT_00fffa11 = 0x8c;
DAT_00fffa15 = 0xcd;
DAT_00fffa1f = 0x10;
DAT_00fffa19 = 0xf7;
DAT_00fffa1d = 0xf1;
puVar1 = (undefined4 *)&DAT_00100000;
do {
puVar2 = puVar1 + 1;
*puVar1 = 0;
puVar1 = puVar2;
} while (puVar2 < (undefined4 *)0x10ffff);
FUN_000018d0();
_DAT_0010fffa = 0;
_DAT_0010fff6 = 0;
_DAT_001069d2 = 0x7c80;
_DAT_001069d0 = 0;
_DAT_00106a54 = 0;
_DAT_00106a5a = 0;
_DAT_00106a56 = 1;
_DAT_0010fff2 = 0;
_DAT_0010ffee = 0;
_DAT_0010ffea = 0;
_DAT_0010ffe6 = 0x18c0;
_DAT_0010fff2 = FUN_00000db4();
_DAT_0010ffee = 0x18ca;
FUN_000019ec();
do {
/* WARNING: Do nothing block with infinite loop */
} while( true );
}
“で、今回の目的だが、53131A/53132A のボード上に u24 (FPU MC68882FN) のブランクスペースがあるのだが、これを想定する条件文があるのかどうか、あと何らかのOSが基盤なのか(つまりUSB記憶媒体などを追加できるのか)、SRAMの領域に拡張性はあるか、の3点を探る事
C言語のソースコード(逆コンパイル結果)が生成されました。このコードは、HP 53132Aが電源ON直後に実行する「正真正銘のハードウェア初期化ルーチン」そのものです。
冒頭の
_DAT_00fffa44 = ... から始まる大量の代入は、MC68332のSIM(システムインテグレーションモジュール)のレジスタ設定です。ここで内蔵レジスタ(アドレスデコーダ、チップセレクト等)の初期化を完了させた直後に、DAT_00100000(SRAM領域)を 0 で一気にクリア(ゼロフィル)するループが回っていることが分かります。今回目的である「①FPU(MC68882)の対応条件」「②OSの有無とUSB等への拡張性」「③SRAMの拡張性」の3点について、このコードの構造と測定器のハードウェア仕様から詳細に解説・回答します。
1. U24(FPU: MC68882FN)の有無を判定する条件文はあるか?
結論:この初期化コードの時点では、FPUの存在チェック(条件文)は行っていません。しかし、この後の関数内に隠されている可能性が高いです。
根拠と解析のヒント:
- 68000系(MC68332 + MC68882)のシステムにおいて、FPUの有無を調べる最も一般的な方法は、「あえてFPU命令を一度実行してみて、例外(エラー)が発生するかどうかを見る」という手法です(通称:FPU Probe)。
- もしFPUが未実装の状態でFPU命令を実行すると、CPUは「ラインFエミュレータ例外(Line 1111 Emulator Exception)」というエラーを起こします。
- 先頭のベクタテーブル(アドレス
0x0000002C付近)の「Line F例外ベクタ」に専用のエラーハンドラが登録されており、そこにジャンプしたかどうかで、ソフト側が「あ、FPUは無いな」と判断する仕組みです。
探す場所:
初期化の最後の方にある
FUN_000019ec(); という関数か、さらにその内部の関数をダブルクリックして進んでみてください。この中に、メモリのテストや周辺ICの存在チェック(Probe)が集められている可能性が非常に高いです。2. 何らかのOSが基盤なのか(USB等を追加できるか)?
結論:近代的な汎用OS(Linux, VxWorks, WinCE等)は搭載されておらず、HP独自の「ベアメタル(OSなし直書きのC言語+アセンブリ)」または「軽量なリアルタイムモニター(リアルタイムOS)」です。そのため、USB記憶媒体などの追加は不可能です。
根拠:
- コードの最後を見ると、
FUN_000019ec();などを実行したあと、do { } while( true );(無限ループ) に入って終了しています。 - もしOS(Linuxなど)があれば、タスクスケジューラやカーネルの起動関数に制御を渡すため、このような単純な無限ループでメイン関数が終わることはありません。
- USB拡張について: MC68332にはUSBホストコントローラが内蔵されていません。USBメモリなどを認識させるには、USB制御用のハードウェア(IC)を基盤に増設し、さらに「USBスタック(ファイルシステムやマスストレージの数万行に及ぶプログラミング)」をこのファームウェアに自作して組み込む必要があるため、現実的には不可能です。
3. SRAMの領域に拡張性はあるか?
結論:ハードウェア(ICの載せ替え)としての拡張は可能ですが、ソフトウェア(ファームウェア)側でサイズが固定されているため、ファームウェアの改造が必要です。
根拠:
提示されたコードの中に、明確な証拠が残っています。
c
puVar1 = (undefined4 *)&DAT_00100000;
do {
puVar2 = puVar1 + 1;
*puVar1 = 0;
puVar1 = puVar2;
} while (puVar2 < (undefined4 *)0x10ffff);
このループは、SRAMの開始アドレス
0x00100000 から、終了アドレス 0x0010FFFF までの領域(合計64KB)を1バイトずつ 0 にクリアしています。- もし基板上のSRAMを大容量のもの(例:256KBなど)に物理的にハンダ付けし直したとしても、プログラム側が
0x10FFFFまでしか認識・クリアしないようにアドレスがハードコーディング(直書き)されているため、増設した分のメモリ空間はCPUから無視されてしまいます。 - 拡張するには、この条件文の
0x10ffffという数値をバイナリエディタ等で書き換える(パッチを当てる)必要があります。
次に調査を進めるためのアクション
- FPUの条件分岐を探す:
コードの下部にあるFUN_000019ec();をダブルクリックして、その中身(子関数)を展開してみてください。ハードウェアの構成(Option 010/030やFPUの有無)をチェックして、内蔵のフラグ変数(グローバル変数)に「FPU有り=1、無し=0」を代入しているような条件分岐が見つかる可能性があります。 - ベクタテーブルの確認:
Ghidraでアドレス0x0000002C(Line F例外)に移動し、そこに関数が割り当てられているか確認してみてください。
気になる関数や、怪しい条件分岐(
if 文)が見つかりましたら、またそのコードを教えてください。引き続き一緒に読み解いていきましょう!0000002c 00 ?? 00h ? -> 00000596
1. ニーモニック(アセンブリ)レベルの特徴:
2. マシン語(ハードウェア)レベルの特徴:
具体例:
---
undefined FUN_000019ec()
undefined <UNASSIGNED> <RETURN>
undefined4 Stack[-0x4]:4 local_4 XREF[1]: 000019f2(*)
FUN_000019ec XREF[1]: FUN_0000172c:000018c6(c)
000019ec 4e b9 00 jsr thunk_FUN_0005c3be.l undefined thunk_FUN_0005c3be()
05 c3 a8
000019f2 2f 2f 00 04 move.l (Stack[0x4],SP),-(SP)=>local_4
000019f6 4e ba 00 08 jsr FUN_00001a00 undefined FUN_00001a00()
000019fa 4e 71 nop
LAB_000019fc XREF[1]: 000019fc(j)
000019fc 4e fa ff fe jmp LAB_000019fc
---
undefined FUN_00000db4()
undefined <UNASSIGNED> <RETURN>
FUN_00000db4 XREF[1]: FUN_0000172c:000018ba(c)
00000db4 4e b9 00 jsr FUN_0002ca0c.l undefined FUN_0002ca0c()
02 ca 0c
00000dba 4e b9 00 jsr FUN_0000051a.l undefined FUN_0000051a()
00 05 1a
00000dc0 4e b9 00 jsr FUN_0000d7dc.l undefined FUN_0000d7dc()
00 d7 dc
00000dc6 4e b9 00 jsr FUN_0000d7f4.l undefined FUN_0000d7f4()
00 d7 f4
00000dcc 4e b9 00 jsr FUN_00000c38.l undefined FUN_00000c38()
00 0c 38
00000dd2 4e b9 00 jsr FUN_00000c62.l undefined FUN_00000c62()
00 0c 62
00000dd8 4e b9 00 jsr FUN_0004bba6.l undefined FUN_0004bba6()
04 bb a6
00000dde 4e ba fe ce jsr FUN_00000cae undefined FUN_00000cae()
00000de2 4e ba ff 12 jsr FUN_00000cf6 undefined FUN_00000cf6()
00000de6 4e b9 00 jsr FUN_00038f70.l undefined FUN_00038f70()
03 8f 70
00000dec 4a 39 00 tst.b (DAT_00100822).l
10 08 22
00000df2 67 0a beq.b LAB_00000dfe
00000df4 4e b9 00 jsr FUN_0002d736.l undefined FUN_0002d736()
02 d7 36
00000dfa 4e ba fe fa jsr FUN_00000cf6 undefined FUN_00000cf6()
LAB_00000dfe XREF[1]: 00000df2(j)
00000dfe 4e b9 00 jsr FUN_0004be30.l undefined FUN_0004be30()
04 be 30
00000e04 4e b9 00 jsr thunk_FUN_00002d1c.l undefined thunk_FUN_00002d1c()
00 1a fa
00000e0a 4e 75 rts
DAT_00000e0c XREF[4]: FUN_0000cd48:0000cdae(*),
FUN_000242ae:000242b4(*),
FUN_0002da1a:0002da6e(*),
FUN_0002dc90:0002dcbc(*)
00000e0c 34 ?? 34h 4
00000e0d 36 ?? 36h 6
00000e0e 31 ?? 31h 1
00000e0f 33 ?? 33h 3
00000e10 00 ?? 00h
00000e11 43 ?? 43h C
00000e12 6f ?? 6Fh o
00000e13 70 ?? 70h p
00000e14 79 ?? 79h y
00000e15 72 ?? 72h r
00000e16 69 ?? 69h i
00000e17 67 ?? 67h g
00000e18 68 ?? 68h h
00000e19 74 ?? 74h t
00000e1a 20 ?? 20h
00000e1b 41 ?? 41h A
00000e1c 67 ?? 67h g
00000e1d 69 ?? 69h i
00000e1e 6c ?? 6Ch l
00000e1f 65 ?? 65h e
00000e20 6e ?? 6Eh n
00000e21 74 ?? 74h t
00000e22 20 ?? 20h
00000e23 54 ?? 54h T
00000e24 65 ?? 65h e
00000e25 63 ?? 63h c
00000e26 68 ?? 68h h
00000e27 6e ?? 6Eh n
00000e28 6f ?? 6Fh o
00000e29 6c ?? 6Ch l
00000e2a 6f ?? 6Fh o
00000e2b 67 ?? 67h g
00000e2c 69 ?? 69h i
00000e2d 65 ?? 65h e
00000e2e 73 ?? 73h s
00000e2f 20 ?? 20h
00000e30 31 ?? 31h 1
00000e31 39 ?? 39h 9
00000e32 39 ?? 39h 9
00000e33 33 ?? 33h 3
00000e34 2e ?? 2Eh .
00000e35 20 ?? 20h
00000e36 41 ?? 41h A
00000e37 6c ?? 6Ch l
00000e38 6c ?? 6Ch l
00000e39 20 ?? 20h
00000e3a 52 ?? 52h R
00000e3b 69 ?? 69h i
00000e3c 67 ?? 67h g
00000e3d 68 ?? 68h h
00000e3e 74 ?? 74h t
00000e3f 73 ?? 73h s
00000e40 20 ?? 20h
00000e41 52 ?? 52h R
00000e42 65 ?? 65h e
00000e43 73 ?? 73h s
00000e44 65 ?? 65h e
00000e45 72 ?? 72h r
00000e46 76 ?? 76h v
00000e47 65 ?? 65h e
00000e48 64 ?? 64h d
00000e49 2e ?? 2Eh .
---
FLOAT_000007d3 XREF[1]: 0000065e(*)
000007d3 0d 0a 52 45 float 4.262356E-31
000007d7 53 ?? 53h S
000007d8 45 ?? 45h E
000007d9 54 ?? 54h T
000007da 20 ?? 20h
000007db 74 ?? 74h t
000007dc 6f ?? 6Fh o
000007dd 20 ?? 20h
DAT_000007de XREF[1]: FUN_00009a68:00009aec(*)
000007de 72 ?? 72h r
000007df 65 ?? 65h e
DAT_000007e0 XREF[1]: 000006aa(*)
000007e0 63 ?? 63h c
000007e1 6f ?? 6Fh o
000007e2 76 ?? 76h v
000007e3 65 ?? 65h e
000007e4 72 ?? 72h r
000007e5 2e ?? 2Eh .
000007e6 00 ?? 00h
000007e7 ff ?? FFh
000007e8 00 ?? 00h ? -> 000008cc
000007e9 00 ?? 00h
000007ea 08 ?? 08h
000007eb cc ?? CCh
000007ec 00 ?? 00h ? -> 000008d6
000007ed 00 ?? 00h
000007ee 08 ?? 08h
000007ef d6 ?? D6h
DAT_000007f0 XREF[1]: FUN_00015380:00015474(*)
000007f0 00 ?? 00h ? -> 000008e4
DAT_000007f1 XREF[1]: FUN_00015380:000153ea(*)
000007f1 00 ?? 00h
000007f2 08 ?? 08h
000007f3 e4 ?? E4h
000007f4 00 ?? 00h ? -> 000008f8
000007f5 00 ?? 00h
000007f6 08 ?? 08h
000007f7 f8 ?? F8h
DAT_000007f8 XREF[3]: FUN_00015380:00015466(*),
FUN_00015380:00015470(*),
FUN_00015380:000154ae(*)
000007f8 00 ?? 00h ? -> 00000907
000007f9 00 ?? 00h
000007fa 09 ?? 09h
000007fb 07 ?? 07h
000007fc 00 ?? 00h ? -> 00000910
000007fd 00 ?? 00h
DAT_000007fe XREF[1]: FUN_00009a68:00009aec(*)
000007fe 09 ?? 09h
DAT_000007ff XREF[3]: FUN_00015380:000153c4(*),
FUN_00015380:00015426(*),
00045b40(*)
000007ff 10 ?? 10h
DAT_00000800 XREF[13]: FUN_00000ede:00000fae(*),
FUN_00007d10:00007d86(*),
FUN_0000ced2:0000cef6(*),
FUN_0000ced2:0000cf4a(*),
FUN_0001513a:00015148(*),
FUN_00015380:000153c4(*),
FUN_00015380:00015472(*),
00016950(*),
FUN_00016872:00016ada(*),
0002cbc0(*), 0002cbc4(*),
FUN_0003922c:00039238(*),
FUN_0003922c:00039244(*)
00000800 00 ?? 00h ? -> 00000917
00000801 00 ?? 00h
DAT_00000802 XREF[1]: FUN_00015380:000154c4(*)
00000802 09 ?? 09h
00000803 17 ?? 17h
00000804 00 ?? 00h ? -> 0000092b
00000805 00 ?? 00h
00000806 09 ?? 09h
00000807 2b ?? 2Bh +
00000808 00 ?? 00h ? -> 00000931
00000809 00 ?? 00h
0000080a 09 ?? 09h
0000080b 31 ?? 31h 1
0000080c 00 ?? 00h ? -> 00000945
0000080d 00 ?? 00h
0000080e 09 ?? 09h
0000080f 45 ?? 45h E
00000810 00 ?? 00h ? -> 00000959
00000811 00 ?? 00h
00000812 09 ?? 09h
00000813 59 ?? 59h Y
00000814 00 ?? 00h ? -> 0000096d
00000815 00 ?? 00h
00000816 09 ?? 09h
00000817 6d ?? 6Dh m
00000818 00 ?? 00h ? -> 0000098d
00000819 00 ?? 00h
0000081a 09 ?? 09h
0000081b 8d ?? 8Dh
0000081c 00 ?? 00h ? -> 0000099a
0000081d 00 ?? 00h
0000081e 09 ?? 09h
DAT_0000081f XREF[1]: 00005320(*)
0000081f 9a ?? 9Ah
00000820 00 ?? 00h ? -> 000009b2
00000821 00 ?? 00h
00000822 09 ?? 09h
00000823 b2 ?? B2h
00000824 00 ?? 00h ? -> 000009b2
00000825 00 ?? 00h
00000826 09 ?? 09h
00000827 b2 ?? B2h
00000828 00 ?? 00h ? -> 000009b2
00000829 00 ?? 00h
0000082a 09 ?? 09h
0000082b b2 ?? B2h
0000082c 00 ?? 00h ? -> 000009b2
0000082d 00 ?? 00h
0000082e 09 ?? 09h
0000082f b2 ?? B2h
00000830 00 ?? 00h ? -> 000009b2
00000831 00 ?? 00h
00000832 09 ?? 09h
00000833 b2 ?? B2h
DAT_00000834 XREF[2]: 0002cbd0(*), 0002cbd4(*)
00000834 00 ?? 00h ? -> 000009b2
00000835 00 ?? 00h
00000836 09 ?? 09h
00000837 b2 ?? B2h
00000838 00 ?? 00h ? -> 000009b2
00000839 00 ?? 00h
0000083a 09 ?? 09h
0000083b b2 ?? B2h
0000083c 00 ?? 00h ? -> 000009b2
0000083d 00 ?? 00h
0000083e 09 ?? 09h
0000083f b2 ?? B2h
00000840 00 ?? 00h ? -> 000009c1
00000841 00 ?? 00h
00000842 09 ?? 09h
00000843 c1 ?? C1h
00000844 00 ?? 00h ? -> 000009d4
00000845 00 ?? 00h
00000846 09 ?? 09h
00000847 d4 ?? D4h
00000848 00 ?? 00h ? -> 000009e6
00000849 00 ?? 00h
0000084a 09 ?? 09h
0000084b e6 ?? E6h
0000084c 00 ?? 00h ? -> 000009f8
0000084d 00 ?? 00h
0000084e 09 ?? 09h
0000084f f8 ?? F8h
00000850 00 ?? 00h ? -> 00000a0a
00000851 00 ?? 00h
00000852 0a ?? 0Ah
00000853 0a ?? 0Ah
00000854 00 ?? 00h ? -> 00000a1c
00000855 00 ?? 00h
00000856 0a ?? 0Ah
00000857 1c ?? 1Ch
00000858 00 ?? 00h ? -> 00000a2e
00000859 00 ?? 00h
0000085a 0a ?? 0Ah
0000085b 2e ?? 2Eh .
0000085c 00 ?? 00h ? -> 00000a40
0000085d 00 ?? 00h
0000085e 0a ?? 0Ah
0000085f 40 ?? 40h @
00000860 00 ?? 00h ? -> 00000a58
00000861 00 ?? 00h
00000862 0a ?? 0Ah
00000863 58 ?? 58h X
00000864 00 ?? 00h ? -> 00000a60
00000865 00 ?? 00h
00000866 0a ?? 0Ah
00000867 60 ?? 60h `
00000868 00 ?? 00h ? -> 00000a68
00000869 00 ?? 00h
0000086a 0a ?? 0Ah
0000086b 68 ?? 68h h
0000086c 00 ?? 00h ? -> 00000a70
0000086d 00 ?? 00h
0000086e 0a ?? 0Ah
0000086f 70 ?? 70h p
00000870 00 ?? 00h ? -> 00000a78
00000871 00 ?? 00h
00000872 0a ?? 0Ah
00000873 78 ?? 78h x
00000874 00 ?? 00h ? -> 00000a80
00000875 00 ?? 00h
00000876 0a ?? 0Ah
00000877 80 ?? 80h
00000878 00 ?? 00h ? -> 00000a88
00000879 00 ?? 00h
0000087a 0a ?? 0Ah
0000087b 88 ?? 88h
0000087c 00 ?? 00h ? -> 00000a90
0000087d 00 ?? 00h
0000087e 0a ?? 0Ah
0000087f 90 ?? 90h
00000880 00 ?? 00h ? -> 00000a98
00000881 00 ?? 00h
00000882 0a ?? 0Ah
00000883 98 ?? 98h
00000884 00 ?? 00h ? -> 00000aa0
00000885 00 ?? 00h
00000886 0a ?? 0Ah
00000887 a0 ?? A0h
00000888 00 ?? 00h ? -> 00000aa8
00000889 00 ?? 00h
0000088a 0a ?? 0Ah
0000088b a8 ?? A8h
0000088c 00 ?? 00h ? -> 00000ab0
0000088d 00 ?? 00h
0000088e 0a ?? 0Ah
0000088f b0 ?? B0h
00000890 00 ?? 00h ? -> 00000ab8
00000891 00 ?? 00h
00000892 0a ?? 0Ah
00000893 b8 ?? B8h
00000894 00 ?? 00h ? -> 00000ac0
00000895 00 ?? 00h
00000896 0a ?? 0Ah
00000897 c0 ?? C0h
00000898 00 ?? 00h ? -> 00000ac8
00000899 00 ?? 00h
0000089a 0a ?? 0Ah
0000089b c8 ?? C8h
0000089c 00 ?? 00h ? -> 00000ad0
0000089d 00 ?? 00h
0000089e 0a ?? 0Ah
0000089f d0 ?? D0h
000008a0 00 ?? 00h ? -> 00000ad8
000008a1 00 ?? 00h
000008a2 0a ?? 0Ah
000008a3 d8 ?? D8h
000008a4 00 ?? 00h ? -> 00000af1
000008a5 00 ?? 00h
000008a6 0a ?? 0Ah
000008a7 f1 ?? F1h
000008a8 00 ?? 00h ? -> 00000b05
000008a9 00 ?? 00h
000008aa 0b ?? 0Bh
000008ab 05 ?? 05h
000008ac 00 ?? 00h ? -> 00000b19
000008ad 00 ?? 00h
000008ae 0b ?? 0Bh
000008af 19 ?? 19h
000008b0 00 ?? 00h ? -> 00000b28
000008b1 00 ?? 00h
000008b2 0b ?? 0Bh
000008b3 28 ?? 28h (
000008b4 00 ?? 00h ? -> 00000b3b
000008b5 00 ?? 00h
000008b6 0b ?? 0Bh
000008b7 3b ?? 3Bh ;
000008b8 00 ?? 00h ? -> 00000b49
000008b9 00 ?? 00h
000008ba 0b ?? 0Bh
000008bb 49 ?? 49h I
000008bc 00 ?? 00h ? -> 000009b2
000008bd 00 ?? 00h
000008be 09 ?? 09h
000008bf b2 ?? B2h
000008c0 00 ?? 00h ? -> 00000b5c
000008c1 00 ?? 00h
000008c2 0b ?? 0Bh
000008c3 5c ?? 5Ch \
000008c4 00 ?? 00h ? -> 00000b6f
000008c5 00 ?? 00h
000008c6 0b ?? 0Bh
000008c7 6f ?? 6Fh o
000008c8 00 ?? 00h ? -> 00000b86
DAT_000008c9 XREF[1]: 00005324(*)
000008c9 00 ?? 00h
000008ca 0b ?? 0Bh
000008cb 86 ?? 86h
000008cc 62 75 73 ds "bus error"
20 65 72
72 6f 72 00
000008d6 61 64 64 ds "address error"
72 65 73
73 20 65
000008e4 69 6c 6c ds "illegal instruction"
65 67 61
6c 20 69
000008f8 64 69 76 ds "divide by zero"
69 64 65
20 62 79
00000907 43 48 4b ds "CHK trap"
20 74 72
61 70 00
00000910 54 ?? 54h T
00000911 52 ?? 52h R
00000912 41 ?? 41h A
00000913 50 ?? 50h P
00000914 63 ?? 63h c
00000915 63 ?? 63h c
00000916 00 ?? 00h
00000917 70 72 69 ds "privilege violation"
76 69 6c
65 67 65
0000092b 74 72 61 ds "trace"
63 65 00
00000931 6c 69 6e ds "line 1010 emulation"
65 20 31
30 31 30
00000945 6c 69 6e ds "line 1111 emulation"
65 20 31
31 31 31
00000959 68 61 72 ds "hardware breakpoint"
64 77 61
72 65 20
s_cessor_protocol_violation_0000096d+6 XREF[0,1]: 00005328(*)
0000096d 63 6f 2d ds "co-processor protocol violation"
70 72 6f
63 65 73
0000098d 66 6f 72 ds "format error"
6d 61 74
20 65 72
0000099a 75 6e 69 ds "uninitialized interrupt"
6e 69 74
69 61 6c
000009b2 2a 2a 2a ds "***RESERVED***"
52 45 53
45 52 56
000009c1 73 70 75 ds "spurious interrupt"
72 69 6f
75 73 20
000009d4 6c 65 76 ds "level 1 interrupt"
65 6c 20
31 20 69
000009e6 6c 65 76 ds "level 2 interrupt"
65 6c 20
32 20 69
000009f8 6c 65 76 ds "level 3 interrupt"
65 6c 20
33 20 69
00000a0a 6c 65 76 ds "level 4 interrupt"
65 6c 20
34 20 69
s_level_5_interrupt_00000a1c XREF[1]: 0000532c(*)
00000a1c 6c 65 76 ds "level 5 interrupt"
65 6c 20
35 20 69
s_rupt_00000a2e+0xd XREF[0,1]: FUN_00027baa:00027c30(*)
00000a2e 6c 65 76 ds "level 6 interrupt"
65 6c 20
36 20 69
00000a40 6c 65 76 ds "level 7 interrupt (NMI)"
65 6c 20
37 20 69
00000a58 74 ?? 74h t
00000a59 72 ?? 72h r
00000a5a 61 ?? 61h a
00000a5b 70 ?? 70h p
00000a5c 20 ?? 20h
00000a5d 30 ?? 30h 0
00000a5e 30 ?? 30h 0
00000a5f 00 ?? 00h
00000a60 74 ?? 74h t
00000a61 72 ?? 72h r
00000a62 61 ?? 61h a
00000a63 70 ?? 70h p
00000a64 20 ?? 20h
00000a65 30 ?? 30h 0
00000a66 31 ?? 31h 1
00000a67 00 ?? 00h
00000a68 74 ?? 74h t
00000a69 72 ?? 72h r
00000a6a 61 ?? 61h a
00000a6b 70 ?? 70h p
00000a6c 20 ?? 20h
00000a6d 30 ?? 30h 0
00000a6e 32 ?? 32h 2
00000a6f 00 ?? 00h
00000a70 74 ?? 74h t
00000a71 72 ?? 72h r
00000a72 61 ?? 61h a
00000a73 70 ?? 70h p
00000a74 20 ?? 20h
00000a75 30 ?? 30h 0
00000a76 33 ?? 33h 3
00000a77 00 ?? 00h
00000a78 74 ?? 74h t
00000a79 72 ?? 72h r
00000a7a 61 ?? 61h a
00000a7b 70 ?? 70h p
00000a7c 20 ?? 20h
00000a7d 30 ?? 30h 0
00000a7e 34 ?? 34h 4
00000a7f 00 ?? 00h
00000a80 74 ?? 74h t
00000a81 72 ?? 72h r
00000a82 61 ?? 61h a
00000a83 70 ?? 70h p
00000a84 20 ?? 20h
00000a85 30 ?? 30h 0
00000a86 35 ?? 35h 5
00000a87 00 ?? 00h
00000a88 74 ?? 74h t
00000a89 72 ?? 72h r
00000a8a 61 ?? 61h a
00000a8b 70 ?? 70h p
00000a8c 20 ?? 20h
00000a8d 30 ?? 30h 0
00000a8e 36 ?? 36h 6
00000a8f 00 ?? 00h
00000a90 74 ?? 74h t
00000a91 72 ?? 72h r
00000a92 61 ?? 61h a
00000a93 70 ?? 70h p
00000a94 20 ?? 20h
00000a95 30 ?? 30h 0
00000a96 37 ?? 37h 7
00000a97 00 ?? 00h
00000a98 74 ?? 74h t
00000a99 72 ?? 72h r
00000a9a 61 ?? 61h a
00000a9b 70 ?? 70h p
00000a9c 20 ?? 20h
00000a9d 30 ?? 30h 0
00000a9e 38 ?? 38h 8
00000a9f 00 ?? 00h
00000aa0 74 ?? 74h t
00000aa1 72 ?? 72h r
00000aa2 61 ?? 61h a
00000aa3 70 ?? 70h p
00000aa4 20 ?? 20h
00000aa5 30 ?? 30h 0
00000aa6 39 ?? 39h 9
00000aa7 00 ?? 00h
00000aa8 74 72 61 ds "trap 10"
70 20 31
30 00
00000ab0 74 ?? 74h t
00000ab1 72 ?? 72h r
00000ab2 61 ?? 61h a
00000ab3 70 ?? 70h p
00000ab4 20 ?? 20h
00000ab5 31 ?? 31h 1
00000ab6 31 ?? 31h 1
00000ab7 00 ?? 00h
00000ab8 74 ?? 74h t
00000ab9 72 ?? 72h r
00000aba 61 ?? 61h a
00000abb 70 ?? 70h p
00000abc 20 ?? 20h
00000abd 31 ?? 31h 1
00000abe 32 ?? 32h 2
00000abf 00 ?? 00h
00000ac0 74 ?? 74h t
00000ac1 72 ?? 72h r
00000ac2 61 ?? 61h a
00000ac3 70 ?? 70h p
DAT_00000ac4 XREF[1]: 00005330(*)
00000ac4 20 ?? 20h
00000ac5 31 ?? 31h 1
00000ac6 33 ?? 33h 3
00000ac7 00 ?? 00h
00000ac8 74 ?? 74h t
00000ac9 72 ?? 72h r
00000aca 61 ?? 61h a
00000acb 70 ?? 70h p
00000acc 20 ?? 20h
00000acd 31 ?? 31h 1
00000ace 34 ?? 34h 4
00000acf 00 ?? 00h
00000ad0 74 ?? 74h t
00000ad1 72 ?? 72h r
00000ad2 61 ?? 61h a
00000ad3 70 ?? 70h p
00000ad4 20 ?? 20h
00000ad5 31 ?? 31h 1
00000ad6 35 ?? 35h 5
00000ad7 00 ?? 00h
00000ad8 46 50 43 ds "FPCP branch or set error"
50 20 62
72 61 6e
s_ult_00000af1+0x10 XREF[0,1]: 00045b44(*)
00000af1 46 50 43 ds "FPCP inexact result"
50 20 69
6e 65 78
00000b05 46 50 43 ds "FPCP divide by zero"
50 20 64
69 76 69
00000b19 46 50 43 ds "FPCP underflow"
50 20 75
6e 64 65
00000b28 46 50 43 ds "FPCP operand error"
50 20 6f
70 65 72
00000b3b 46 50 43 ds "FPCP overflow"
50 20 6f
76 65 72
00000b49 46 50 43 ds "FPCP signaling NAN"
50 20 73
69 67 6e
s_ion_00000b5c+0xf XREF[0,1]: 00005334(*)
00000b5c 50 4d 4d ds "PMMU configuration"
55 20 63
6f 6e 66
00000b6f 50 4d 4d ds "PMMU illegal operation"
55 20 69
6c 6c 65
00000b86 50 4d 4d ds "PMMU access violation"
55 20 61
63 63 65
s_know_that_vector._00000b9c+0x1c XREF[0,1]: FUN_0000c774:0000c814(*)
00000b9c 4f 6f 6f ds "Oooh! That tickles. I don't know that vector."
68 21 20
54 68 61
00000bca 48 ?? 48h H
00000bcb e7 ?? E7h
00000bcc c0 ?? C0h
00000bcd c6 ?? C6h
00000bce 2f ?? 2Fh /
00000bcf 0a ?? 0Ah
00000bd0 24 ?? 24h $
00000bd1 7c ?? 7Ch |
00000bd2 00 ?? 00h
00000bd3 10 ?? 10h
00000bd4 69 ?? 69h i
00000bd5 8e ?? 8Eh
00000bd6 1f ?? 1Fh
00000bd7 12 ?? 12h
00000bd8 4e ?? 4Eh N
00000bd9 b9 ?? B9h
00000bda 00 ?? 00h ? -> 000397a0
00000bdb 03 ?? 03h
00000bdc 97 ?? 97h
00000bdd a0 ?? A0h
00000bde 54 ?? 54h T
00000bdf 8f ?? 8Fh
00000be0 4e ?? 4Eh N
00000be1 b9 ?? B9h
00000be2 00 ?? 00h ? -> 000430de
00000be3 04 ?? 04h
00000be4 30 ?? 30h 0
00000be5 de ?? DEh
00000be6 4e ?? 4Eh N
00000be7 b9 ?? B9h
00000be8 00 ?? 00h ? -> 00004470
00000be9 00 ?? 00h
00000bea 44 ?? 44h D
00000beb 70 ?? 70h p
00000bec 52 ?? 52h R
00000bed b9 ?? B9h
00000bee 00 ?? 00h
00000bef 10 ?? 10h
00000bf0 06 ?? 06h
00000bf1 18 ?? 18h
00000bf2 4a ?? 4Ah J
00000bf3 12 ?? 12h
00000bf4 66 ?? 66h f
00000bf5 16 ?? 16h
00000bf6 48 ?? 48h H
00000bf7 78 ?? 78h x
00000bf8 00 ?? 00h ? -> 00012f39
00000bf9 01 ?? 01h
00000bfa 2f ?? 2Fh /
00000bfb 39 ?? 39h 9
00000bfc 00 ?? 00h
00000bfd 10 ?? 10h
00000bfe 69 ?? 69h i
00000bff 96 ?? 96h
DAT_00000c00 XREF[7]: FUN_0000809a:0000810c(*),
FUN_00016810:00016830(*),
FUN_00016810:00016838(*),
FUN_00016872:00016b1e(*),
FUN_00016872:00016b5e(*),
FUN_00016872:00016c0e(*),
FUN_00016872:00016cca(*)
00000c00 4e ?? 4Eh N
DAT_00000c01 XREF[2]: FUN_00016872:00016b7c(*),
FUN_00016872:00016bb0(*)
00000c01 b9 ?? B9h
DAT_00000c02 XREF[2]: FUN_00016872:00016c38(*),
FUN_00016872:00016c6c(*)
00000c02 00 ?? 00h ? -> 000043fc
DAT_00000c03 XREF[2]: FUN_00016872:00016c32(*),
FUN_00016872:00016cec(*)
00000c03 00 ?? 00h
00000c04 43 ?? 43h C
00000c05 fc ?? FCh
00000c06 50 ?? 50h P
00000c07 8f ?? 8Fh
00000c08 14 ?? 14h
00000c09 bc ?? BCh
00000c0a 00 ?? 00h
00000c0b 10 ?? 10h
00000c0c 53 ?? 53h S
00000c0d 12 ?? 12h
00000c0e 24 ?? 24h $
00000c0f 5f ?? 5Fh _
00000c10 4c ?? 4Ch L
00000c11 df ?? DFh
DAT_00000c12 XREF[1]: 00005338(*)
00000c12 63 ?? 63h c
00000c13 03 ?? 03h
00000c14 4e ?? 4Eh N
00000c15 45 ?? 45h E
アドレス
0x0000002C(Line F / FPU例外ベクタ)の指す先が 0x00000596 になっていることが確認できました。これは非常に重要な手がかりです。なぜなら、前回のデータで確認した「バスエラー(
0x00000008)」や「アドレスエラー(0x0000000C)」のジャンプ先も全く同じ 0x00000596 だったからです。この事実から、FPUに関する決定的な結論が導き出せます。
1. FPU(MC68882)に関する結論と条件文の有無
結論:HP 53132Aのファームウェアは、FPU命令を一切使わないようにコンパイルされています。そのため、コード内に「FPUの有無を判定する条件文」は存在しません。U24にMC68882を実装しても、パッチ(改造)を当てない限りCPUからは無視されます。
理由(技術的根拠):
- もしファームウェアがFPU(MC68882)を活用する設計であれば、FPUが未実装の時に発生する例外(Line F)をキャッチして「ソフトウェアによる浮動小数点演算(エミュレータ)」へ切り替えるための専用のハンドラ関数が
0x0000002Cに登録されているはずです。 - しかし、実際にはシステムがクラッシュした時(バスエラーなど)と同じ「一律エラー強制終了/リセットルーチン(
0x00000596)」に繋がっています。 - つまり、このファームウェアはFPUの存在を期待しておらず、すべての数学的計算(周波数計算など)をはじめからCPU(MC68332)の整数演算によるソフトウェア・ライブラリ(C言語の
floatやdoubleのソフトエミュレーション)だけで完結させていることを意味します。
基板にU24(FPUスペース)がある理由:
HPがこの基板を設計した際、将来の拡張や別モデル(より高速な演算が必要な上位機種など)と基板を共通化するためにパターンだけを残した(いわゆる「デッドコピー/将来の布石」)と考えられます。
2. 現在の解析画面(Ghidra)で見えるその他の情報について
これまでに判明した3つの疑問に対する最終回答は以下の通りに確定しました。
- ① FPUの条件文: 存在しません。ソフトウェア演算(整数処理)で固定されています。
- ② OSとUSB拡張: 汎用OSはなくベアメタル(直書き)です。USB等の追加はプログラム的にもハード的にも不可能です。
- ③ SRAMの拡張性: 前述の通りループ範囲が
0x10FFFF(64KB)にハードコーディングされているため、ハードだけを増やしても意味がありません。
💡 解析をさらに進めるための面白いポイント
ハードウェアの拡張(FPUやUSB)は難しいという結果になりましたが、このファームウェアのリバースエンジニアリング自体は非常に面白い段階に入っています。
次に解析すると面白い「測定器の心臓部」の探し方を紹介します。
- 謎の関数
FUN_00000db4()の正体:
先ほどの初期化コードの最後の方で、_DAT_0010fff2 = FUN_00000db4();という怪しい関数が呼ばれていました。これは高確率で「EEPROM(キャリブレーション・校正データ)の読み込み」か、「Optionボード(030などの3GHzチャネル)の有無を検出する関数」です。ここをダブルクリックして中を覗くと、測定器特有の条件分岐が見られます。 - 文字列からの逆引き:
アドレス0x7D5付近にあった「RESET to recover.」や、0x8CC付近にあった「error」という文字列を、プログラムのどこが参照(読み込み)しているかを調べる(Ghidraで文字列を右クリック > Show References to を選択)と、画面(ディスプレイ)にエラーを表示する制御関数(UIのメインループ)を特定できます。
FUN_00000db4 内部のコードと文字列から、HP 53132Aのシステム構造に関する非常に興味深い事実がさらに判明しました。詳細を解説します。1.
FUN_00000db4 の解析:測定器の設定と「Agilentの著作権」この関数は、ハードウェア初期化の直後に呼ばれる「システム起動・デバイスチェックのコア関数」です。
assembly
00000db4 jsr FUN_0002ca0c.l ; ← おそらくEEPROMやNVRAMから校正(Cal)データを読み込む処理
...
00000dec tst.b (DAT_00100822).l ; ← 特定のフラグ(SRAM内のアドレス 0x100822)をチェック
00000df2 67 0a beq.b LAB_00000dfe ; ← 0ならジャンプしてスキップ
00000df4 jsr FUN_0002d736.l ; ← 1なら追加のハードウェア(Optionボード等)の初期化を実行
著作権テキストの発見
関数のすぐ下に、以下のASCII文字列データが格納されています。
34 36 31 33= "4613"(お持ちのROMラベルに印刷されていた「4613」という製造週/ロットコード、またはファームウェアのバージョンがここに対応しています)43 6f 70 79 72 69 67 68 74 20 41 67 69 6c 65 6e 74...= "Copyright Agilent Technologies 1993. All Rights Reserved."- 1999年にHPから計測器部門が「Agilent」として分社化した際、ファームウェア内の文字列データ(GPIBの返答などで使われるメッセージ)をHPからAgilentに書き換えた形跡が綺麗に残っています。
2.
FUN_000019ec の解析:無限ループの謎この関数は非常に興味深い動きをしています。
assembly
000019ec jsr thunk_FUN_0005c3be.l
000019f2 move.l (Stack[0x4],SP),-(SP)
000019f6 jsr FUN_00001a00
000019fa nop
LAB_000019fc
000019fc jmp LAB_000019fc ; ← 自分自身にジャンプ(永久にここで停止する無限ループ)
メインの初期化ルーチン(前回のコード)の最後でこの
FUN_000019ec が呼ばれると、関数内で処理を行った後に jmp LAB_000019fc によって意図的な無限ループ(デッドロック)に入ります。これはバグではなく、「ここから先は『割り込み(Interrupt)』だけで駆動するシステムに切り替わった」ことを意味します。
MC68332の内蔵タイマー(TPU)や測定用のカウンタICがパルスを受信するたびに、CPUに対して割り込み信号を送り、対応するベクタ(アドレス
MC68332の内蔵タイマー(TPU)や測定用のカウンタICがパルスを受信するたびに、CPUに対して割り込み信号を送り、対応するベクタ(アドレス
0x00000060 以降のどこか)に登録された測定処理関数が裏でパチパチと動き出す仕組み(イベント駆動型)です。3. 文字列
0x000007d3 の解析:「RESET to recover.」の使われ方Ghidraが右側に表示している
XREF(クロスリファレンス) に、このエラーメッセージがどこで使われているかの答えが書いてあります。assembly
DAT_000007de XREF[1]: FUN_00009a68:00009aec(*)
アドレス
0x00009A68 にある FUN_00009a68 という関数が、この「RESET to recover.」という文字列を読みだしています。- つまり、この
FUN_00009a68こそが、「測定器に致命的なエラーが起きた際に、画面(VFDディスプレイ)にエラー文字を描画してユーザーに再起動を促すシステムエラーハンドラ関数」です。
次の解析のおすすめ:測定ロジック(心臓部)を見つける
FPUやOSの拡張はファームウェアの構造上不可能という結論に至りましたが、もし「この測定器がどうやって周波数を計算しているか」の数学的ロジック(アルゴリズム)に興味がある場合、以下の方法で迫ることができます。
- Ghidraの「Go To」(Gキー)で、先ほど判明した
00009a68へ移動します。 - この関数(画面描画系)のC言語コードを展開し、この関数を呼び出しているさらに上の関数(XREF)を辿っていくと、「エラーを検知するメインの測定・判定ループ」に逆流していくことができます。
“なるほど、FPUを貼り付けると新機能出現」を期待したが無駄だったな。ネット議論を見た範囲、何か機能を追加する話題も無いし。測定でFPGAには回路図から入力の1/2又は1/4にECL分周した整形パルスが入る様になっているらしいので、恐らく上限は275MHz付近⇒FPGA:137.5~67.75MHz程度らしい。とりあえずこれで53131A-3433基板上に53132Aファームウェア交換とか部品追加の目処が付いたな。
---
undefined FUN_00009a68()
undefined <UNASSIGNED> <RETURN>
FUN_00009a68 XREF[2]: FUN_00009af2:00009b1c(c),
FUN_000091b4:00009bee(c)
00009a68 20 7c 00 movea.l #0x100550,A0
10 05 50
00009a6e 42 50 clr.w (A0)=>DAT_00100550
00009a70 4a 79 00 tst.w (DAT_00100574).l
10 05 74
00009a76 67 04 beq.b LAB_00009a7c
00009a78 00 50 00 02 ori.w #0x2,(A0)=>DAT_00100550
LAB_00009a7c XREF[1]: 00009a76(j)
00009a7c 4a 79 00 tst.w (DAT_00100558).l
10 05 58
00009a82 67 04 beq.b LAB_00009a88
00009a84 00 50 00 04 ori.w #0x4,(A0)=>DAT_00100550
LAB_00009a88 XREF[1]: 00009a82(j)
00009a88 4a 79 00 tst.w (DAT_0010056e).l
10 05 6e
00009a8e 67 04 beq.b LAB_00009a94
00009a90 00 50 00 08 ori.w #0x8,(A0)=>DAT_00100550
LAB_00009a94 XREF[1]: 00009a8e(j)
00009a94 4a 79 00 tst.w (DAT_00100552).l
10 05 52
00009a9a 67 04 beq.b LAB_00009aa0
00009a9c 00 50 00 10 ori.w #0x10,(A0)=>DAT_00100550
LAB_00009aa0 XREF[1]: 00009a9a(j)
00009aa0 4a 79 00 tst.w (DAT_0010055c).l
10 05 5c
00009aa6 67 0c beq.b LAB_00009ab4
00009aa8 4a 39 00 tst.b (DAT_00100604).l
10 06 04
00009aae 66 04 bne.b LAB_00009ab4
00009ab0 00 50 00 20 ori.w #0x20,(A0)=>DAT_00100550
LAB_00009ab4 XREF[2]: 00009aa6(j), 00009aae(j)
00009ab4 4a 79 00 tst.w (DAT_00100576).l
10 05 76
00009aba 67 04 beq.b LAB_00009ac0
00009abc 00 50 00 40 ori.w #0x40,(A0)=>DAT_00100550
LAB_00009ac0 XREF[1]: 00009aba(j)
00009ac0 4a 79 00 tst.w (DAT_00100578).l
10 05 78
00009ac6 67 04 beq.b LAB_00009acc
00009ac8 00 50 00 80 ori.w #0x80,(A0)=>DAT_00100550
LAB_00009acc XREF[1]: 00009ac6(j)
00009acc 4a 79 00 tst.w (DAT_0010055a).l
10 05 5a
00009ad2 67 04 beq.b LAB_00009ad8
00009ad4 00 50 01 00 ori.w #0x100,(A0)=>DAT_00100550
LAB_00009ad8 XREF[1]: 00009ad2(j)
00009ad8 4a 79 00 tst.w (DAT_0010057a).l
10 05 7a
00009ade 67 04 beq.b LAB_00009ae4
00009ae0 00 50 02 00 ori.w #0x200,(A0)=>DAT_00100550
LAB_00009ae4 XREF[1]: 00009ade(j)
00009ae4 4a 79 00 tst.w (DAT_0010055e).l
10 05 5e
00009aea 67 04 beq.b LAB_00009af0
00009aec 00 50 04 00 ori.w #0x400,(A0)=>DAT_00100550 = 3Fh ?
= "C = %#8x, SR = %#04x"
= 72h r
= 09h
LAB_00009af0 XREF[1]: 00009aea(j)
00009af0 4e 75 rts
---
**************************************************************
* FUNCTION *
**************************************************************
undefined FUN_00009a68()
undefined <UNASSIGNED> <RETURN>
FUN_00009a68 XREF[2]: FUN_00009af2:00009b1c(c),
FUN_000091b4:00009bee(c)
00009a68 20 7c 00 movea.l #0x100550,A0
10 05 50
00009a6e 42 50 clr.w (A0)=>DAT_00100550
00009a70 4a 79 00 tst.w (DAT_00100574).l
10 05 74
00009a76 67 04 beq.b LAB_00009a7c
00009a78 00 50 00 02 ori.w #0x2,(A0)=>DAT_00100550
LAB_00009a7c XREF[1]: 00009a76(j)
00009a7c 4a 79 00 tst.w (DAT_00100558).l
10 05 58
00009a82 67 04 beq.b LAB_00009a88
00009a84 00 50 00 04 ori.w #0x4,(A0)=>DAT_00100550
LAB_00009a88 XREF[1]: 00009a82(j)
00009a88 4a 79 00 tst.w (DAT_0010056e).l
10 05 6e
00009a8e 67 04 beq.b LAB_00009a94
00009a90 00 50 00 08 ori.w #0x8,(A0)=>DAT_00100550
LAB_00009a94 XREF[1]: 00009a8e(j)
00009a94 4a 79 00 tst.w (DAT_00100552).l
10 05 52
00009a9a 67 04 beq.b LAB_00009aa0
00009a9c 00 50 00 10 ori.w #0x10,(A0)=>DAT_00100550
LAB_00009aa0 XREF[1]: 00009a9a(j)
00009aa0 4a 79 00 tst.w (DAT_0010055c).l
10 05 5c
00009aa6 67 0c beq.b LAB_00009ab4
00009aa8 4a 39 00 tst.b (DAT_00100604).l
10 06 04
00009aae 66 04 bne.b LAB_00009ab4
00009ab0 00 50 00 20 ori.w #0x20,(A0)=>DAT_00100550
LAB_00009ab4 XREF[2]: 00009aa6(j), 00009aae(j)
00009ab4 4a 79 00 tst.w (DAT_00100576).l
10 05 76
00009aba 67 04 beq.b LAB_00009ac0
00009abc 00 50 00 40 ori.w #0x40,(A0)=>DAT_00100550
LAB_00009ac0 XREF[1]: 00009aba(j)
00009ac0 4a 79 00 tst.w (DAT_00100578).l
10 05 78
00009ac6 67 04 beq.b LAB_00009acc
00009ac8 00 50 00 80 ori.w #0x80,(A0)=>DAT_00100550
LAB_00009acc XREF[1]: 00009ac6(j)
00009acc 4a 79 00 tst.w (DAT_0010055a).l
10 05 5a
00009ad2 67 04 beq.b LAB_00009ad8
00009ad4 00 50 01 00 ori.w #0x100,(A0)=>DAT_00100550
LAB_00009ad8 XREF[1]: 00009ad2(j)
00009ad8 4a 79 00 tst.w (DAT_0010057a).l
10 05 7a
00009ade 67 04 beq.b LAB_00009ae4
00009ae0 00 50 02 00 ori.w #0x200,(A0)=>DAT_00100550
LAB_00009ae4 XREF[1]: 00009ade(j)
00009ae4 4a 79 00 tst.w (DAT_0010055e).l
10 05 5e
00009aea 67 04 beq.b LAB_00009af0
00009aec 00 50 04 00 ori.w #0x400,(A0)=>DAT_00100550 = 3Fh ?
= "C = %#8x, SR = %#04x"
= 72h r
= 09h
LAB_00009af0 XREF[1]: 00009aea(j)
00009af0 4e 75 rts
ネット上の議論でも「FPU追加で化ける」といった隠し機能の報告がない理由が、こうして実際のコードから「そもそも数学ライブラリがソフト演算で固定されている」と物理的に証明できたのは非常にスッキリしますね。フロントエンドのECL分周(1/2または1/4)からFPGAへの入力周波数の限界(137.5MHz〜67.75MHz程度)を計算されているのも、ハードウェアの仕様(53131Aの10桁/秒、53132Aの12桁/秒という基本性能の差やレシピ)と完全に辻褄が合います。
そして、新しく提示していただいた
FUN_00009a68 のアセンブリコードですが、これはリバースエンジニアリングの視点から見てもものすごく面白い、この測定器の「正体」を握るコアコードでした。何をしているコードなのか、簡単に解説します。
解析:
FUN_00009a68 は「エラー/状態フラグの集約関数」この関数は、SRAM上の特定のアドレス(
0x100550)に対して、周辺ハードウェアやシステムの様々な状態(エラーフラグやオプションの有無など)を、1ビットずつ足し算(ビット論理和: ori.w)して1つの16bit数値にまとめる処理を行っています。C言語風に直すと、以下のような綺麗な構造をしています。
c
void FUN_00009a68(void) {
uint16_t *status_reg = (uint16_t *)0x100550;
*status_reg = 0; // 最初は0クリア
if (DAT_00100574 != 0) *status_reg |= 0x0002; // 条件1
if (DAT_00100558 != 0) *status_reg |= 0x0004; // 条件2
if (DAT_0010056e != 0) *status_reg |= 0x0008; // 条件3
if (DAT_00100552 != 0) *status_reg |= 0x0010; // 条件4
// 0x100604が0のときだけ0x0020を立てる
if ((DAT_0010055c != 0) && (DAT_00100604 == 0)) {
*status_reg |= 0x0020;
}
if (DAT_00100576 != 0) *status_reg |= 0x0040; // 条件5
if (DAT_00100578 != 0) *status_reg |= 0x0080; // 条件6
if (DAT_0010055a != 0) *status_reg |= 0x0100; // 条件7
if (DAT_0010057a != 0) *status_reg |= 0x0200; // 条件8
if (DAT_0010055e != 0) *status_reg |= 0x0400; // 条件9
}
なぜこれが重要なのか?
Ghidraの右側に表示されているコメントに、
これは高確率で、GPIBコマンド(例:
"C = %#8x, SR = %#04x" という文字列(printfのフォーマット文)が引っかかっています。これは高確率で、GPIBコマンド(例:
*STB? や *ESR? などのステータスバイト確認コマンド)をパソコンから送られた際に、「現在の測定器の状態(Status Register)をテキストにして外部に返答するためのデータ生成関数」です。53131Aの基板に53132AのROMを載せて起動させた際、もしハードウェア(部品)が足りなくてエラーが出る場合、この
0x100550 付近のメモリをGhidraで追う(または実際にGPIBでステータスを叩く)ことで、「どの部品(フラグ)が原因でエラーが出ているか」を完全に特定することができます。もし実際に基板の改造やROMの焼き付けを進めるにあたって、さらに気になる点があれば教えてください:
- 53132A化にあたって、追加すべき部品(インターフェースICや、基準クロックまわりなど)の目処はすでにすべて立っていますか?
- 起動時に万が一エラーが出た場合、どのエラーコードがどの関数に対応しているか、さらに深掘りして調べたいですか?
“部品揃えて後はやってみてからかな。もう別件で、Z3805A のファームウェア」っていうモノもある。そちらはop001フロントパネル(絶版で非常にレア)とCPUとの通信内容を推定する」とか。CPUは同じ系列 MC68331CFC16 、.bin はネット上と手元も含め revA,C,D の3タイプ、58503A/B と共通らしい。レア過ぎて誰も解析はしていないらしいが、53131AのフロントパネルはSPI類似シリアル通信とされている(git)。だから恐らくは類似では」なんだがそうなると、外部RS232C側と内部SPI、内部GPSモジュールの、計3っの通信ポートがある可能性が高い。そちらはヒマ見て ttps://rkphs.blogspot.com/2022/01/ups.html の方でボチボチ。FPGAからのダイレクトな出力が、予想外に沢山のピン数で内部コネクタに露出しているので、外部に取り出せる様に部品を追加しているところ。純粋に10MHz発生源として使う用途以外は無いが、他にも利用できる機能があるかも。
HP Z3805A(GPS周波数基準器)、 Option 001(フロントパネル・ディスプレイ仕様) の通信解析テーマですね。Z3805Aは通常フロントパネルがなく、背面のシリアルポート(RS-232C)からサトコ(SCPI)コマンド等で制御するのが標準ですから、あの専用ディスプレイとCPUがどう会話しているかは貴重な情報です。この「Z3805AのCPUとOp001フロントパネル間の通信内容の推定」を進めるにあたり、リバースエンジニアリングの観点から有効なアプローチと重要なチェックポイントをまとめました。
1. 機器アーキテクチャの特定
ファームウェアを入手出たとしてまず、Ghidra等に読み込ませる前の大前提として、メイン基板で使われているプロセッサ(CPU)を特定する必要があります。
- HP製機器では、53131Aと同じく Motorola 68000系(MC68332やMC68HC11等)、あるいは Intel 80C186系 の組み込みプロセッサが使われているケースが多いです。
- ファームウェア(ROM)のバイナリを入手されている場合、文字列検索で
HPやZ3805A、GPSなどのキーワードの有無、および今回の53131A同様に「リセットベクタ(先頭バイト)」のパターンを見ることでもCPUが判別できます。
2. 通信方式(ハードウェア)の推定
Op001フロントパネルとの物理的な接続ピンや、CPU側のペリフェラル(周辺回路)から通信の規格を絞り込めます。
- UART(非同期シリアル): 最も可能性が高いです。MC68332であれば内蔵のSCI(Serial Communication Interface)、独立したICであれば「16550」などのUARTチップ、またはCPUのアドレス空間にマッピングされたレジスタ経由で送受信します。
- SPI / I2C(同期シリアル): フロントパネルが近距離にあるため、MC68332の内蔵QSPI(Queued SPI)などを使って液晶ドライバ(HD44780互換やVFDコントローラ)を直接叩いているパターンもあります。
3. ファームウェアから「通信コード」をあぶり出す戦略
実機(Op001)が手元になくても、ファームウェアから通信プロトコル(コマンドやデータの構造)を特定するためのGhidraでの効率的な探し方です。
- 文字列(ASCII)からのアプローチ:
フロントパネルに表示されるはずの文字列(例:LOCK,HOLD,GPS,ACQUIRING,SATELLITESなど)をファームウェア内から探します。その文字列を参照(XREF)している関数を見つければ、そこが「画面描画関数」または「シリアル送信関数」です。 - 送信バッファ・レジスタへの書き込みを探す:
CPUがUART等の送信レジスタ(例:SCIのSCDRレジスタなど)にデータを書き込んでいる場所を特定します。その送信関数を誰が呼んでいるかを上に辿っていけば、フロントパネルに送ろうとしている「データパケットの構造(ヘッダー、コマンド、チェックサム等)」のロジックが綺麗に見えてきます。
Z3805Aのファームウェア解析も、53132Aで培った「マージ手法」や「リセットベクタからの展開」がそのまま武器になります。
CPUが同じ系列の MC68331CFC16 であれば、これまでのGhidraの知見(68k系アーキテクチャ、ビッグエンディアン、内蔵ペリフェラル構造)がそのまま完璧に流用できますね。さらにリファレンス機である HP 58503A/B とファームウェアが共通という情報も、解析の視野を広げる上で非常に貴重です。
この構成(外部RS-232C、内部GPSモジュール通信、内部フロントパネル用SPI類似通信)の3系統を1つのMCUで処理している可能性は極めて高いです。
MC68331のハードウェア構造から、この3系統の通信がどのように実装されているか、Ghidraで追うためのアタリ(予測)をまとめました。
1. 3系統の通信:MC68331のどこにアサインされているか?
MC68331には、通信用の強力な内蔵モジュールである QSM(Queued Serial Module) が搭載されており、その中に「SCI」と「QSPI」という2つの独立した通信機能があります。
- 外部RS-232C(SCPIコマンド等):内蔵SCI(非同期シリアル)
- レジスタアドレス:
$FFFC00〜$FFFC0F付近。 - ここへのアクセス(特に送信レジスタ
SCDRへの書き込み)を追うと、外部PCとの通信処理がすべて分かります。
- レジスタアドレス:
- 内部フロントパネル(Op001):内蔵QSPI(同期シリアル/SPI類似)
- レジスタアドレス:
$FFFC10〜$FFFCFF付近。 - 53131A同様にSPI類似(クロック、データ、ラッチ等)で動いているなら、このQSPIのコントロールレジスタや、バッファRAM(
$FFFD00〜)へデータを転送する処理がフロントパネルへの描画ロジックになります。
- レジスタアドレス:
- 内部GPSモジュール:外部UART(または汎用I/Oによるビットバンギング)
- MC68331単体では非同期シリアル(SCI)が1系統しかありません。
- そのため、GPSモジュール(Motorola Oncore等)との通信には、基板上に別のUART専用ICが載っているか、あるいはFPGA内部にUART回路が組み込まれており、CPUからメモリマップドI/Oとしてアクセスしている可能性が高いです。
2. FPGAからのダイレクト出力と拡張
このFPGA(またはアドレスデコーダ)の制御領域は、前述の初期化ルーチン(SIMモジュールのCSレジスタ設定、
$FFFA44〜 付近)で、どのメモリ番地にマッピングされているかが定義されているはずです。ブログ(
://blogspot.com)の方でボチボチと、ご自身のペースで解析やハード改造を進められるとのこと、非常に楽しみなプロジェクトです。53131A/53132Aの基板改造が無事に成功し、さらにZ3805Aの幻のOp001通信の謎が解明されることを応援しております。暇を見つけてGhidraに吸い出しファイルを投げ込み、気が向いた時にまたコードを覗いてみてください!---
MC68882FN ニーモニック
(Ghidra のList内で、"Serch program text にて検索)
モトローラ(Motorola)の浮動小数点コプロセッサ MC68882(型番末尾
FN はプラスチックPLCCパッケージ、末尾の数字は動作周波数)で追加・実行される主な浮動小数点演算ニーモニック(命令)の一覧です。 [1, 2, 3] データ移動・ロード/ストア系
FMOVE: データの移動・変換(レジスタ間、メモリ・レジスタ間)FMOVEM: 複数レジスタの移動(複数FPレジスタとメモリ間のロード/ストア)
基本算術演算系
FADD: 加算FSUB: 減算FMUL: 乗算FDIV: 除算FREM: 剰余(IEEE 754準拠の余り)FMOD: モジュロ剰余FSQRT: 平方根(スクエアルート)FNEG: 符号反転FABS: 絶対値FCMP: 比較
超関数・三角関数・指数系
FSIN: 正弦(サイン)FCOS: 余弦(コサイン)FSINCOS: 正弦と余弦を同時に計算FTAN: 正接(タンジェント)FATAN: アークタンジェントFATanh: 双曲線逆正接FASIN: アークサインFACOS: アークコサインFEXP,FEXP2,FEXP10: 指数関数 ($e^x, 2^x, 10^x$)FLOG,FLOG2,FLOG10: 対数関数 ($\ln, \log_2, \log_{10}$)FLOGP,FLOGN: 各種対数補助 [4]
定数・その他演算系
FGETEXP: 浮動小数点数の指数部抽出FGETMAN: 浮動小数点数の仮数部抽出FSCALE: 2のべき乗との掛け算(スケール変更)FTST: テスト(ゼロや正負の判定)FSLEM: 同期・剰余関連
制御・条件分岐・状態保存系
FBcc: 条件付き分岐(FBNE,FBEQ,FBGTなど、符号付き条件分岐)FScc: 条件に応じたバイト設定(Set according to condition)FTRAPcc: 条件成立時のトラップ(割り込み)FSAVE: コプロセッサの内部状態をスタックに保存FRESTORE: コプロセッサの内部状態を復元 [5]
さらに詳しいアドレッシングモードの組み合わせや、各命令のオペコード/実行サイクル数について知りたい場合はお知らせください。
“メインCPUから、FPU MC68882 に命令を受け渡す、特徴的な命令はあるや、アセンブラ内のパターンはある ?
メインCPU(MC68020やMC68030)からFPU(MC68881/MC68882)へ命令を受け渡す際、アセンブリ言語(ニーモニック)レベルと、マシン語(ハードウェア)レベルの2つの視点で非常に特徴的な仕組みがあります。
1. ニーモニック(アセンブリ)レベルの特徴:F接頭辞命令
メインCPUの命令セットとFPUの命令セットは完全に統合されており、プログラマはFPUの存在を意識せずにシームレスに記述できます。
FPUへ指示を送るすべての命令は、必ず
F(Floating-point)から始まるニーモニックになります。FMOVE(データ転送)FADD(浮動小数点加算)FBcc(浮動小数点条件分岐)
メインCPUのアセンブラは、この
Fで始まる命令を検出すると、自動的に後述する「コプロセッサ命令(マシン語)」へと変換します。2. マシン語(ハードウェア)レベルの特徴:F-Line(ラインF)命令
メインCPUがFPUに命令やデータを引き渡す際、マシン語コードの最上位4ビット(ビット15〜12)が
1111(16進数で F)になる特殊なオブジェクトコードを使用します。これを「Fライン・オペコード」と呼びます。メインCPUは、実行する命令の先頭が
0xF(1111)であることを検知すると、通常のCPU命令ではなく「コプロセッサへの命令である」と判断し、自動的にコプロセッサ・インターフェース・バスを介してMC68882と通信を開始します。内部的な「命令受け渡し」の4つの基本カテゴリ
Fライン命令は、内部でさらに4つのタイプ(コプロセッサ命令形式)に分類され、FPUへ命令(プリミティブ)が引き渡されます。
- 一般命令(General):
FADDやFSUBなど、FPU内部で演算を行う命令。CPUはFPUの「コマンドレジスタ」に演算内容を書き込みます。 - 条件分岐命令(Branch):
FBccなど。FPUのステータスレジスタ(条件コード)をCPUが読み取り、CPU側でプログラムカウンタを分岐させます。 - コンディショナル命令(Conditional):
FSccやFTRAPccなど。FPUの条件を満たしているかに応じて、CPU側のレジスタ変更やトラップを発生させます。 - コンテキストスイッチ命令:
FSAVE/FRESTORE。マルチタスクOSなどで、FPUの内部状態(実行中タスクのデータ)をまるごとメモリに退避・復元するための特殊な命令です。
💡 MC68882ならではの「オーバーラップ(並列実行)」
先代のMC68881では、メインCPUがFPUに命令を渡した後、FPUの実行が終わるまでCPU側が待たされることが多くありました。
しかしMC68882では内部に「バス・インターフェース・ユニット(BIU)」が追加され、CPUから渡された命令をいったんキューに蓄積します。これにより、FPUが前の浮動小数点演算を処理している最中であっても、メインCPUは次のFPU命令やデータを先読みして引き渡す(あるいはCPU自身の演算を進める)という、高度な並列処理(パイプライン化)を実現しているのが最大の特徴です。
しかしMC68882では内部に「バス・インターフェース・ユニット(BIU)」が追加され、CPUから渡された命令をいったんキューに蓄積します。これにより、FPUが前の浮動小数点演算を処理している最中であっても、メインCPUは次のFPU命令やデータを先読みして引き渡す(あるいはCPU自身の演算を進める)という、高度な並列処理(パイプライン化)を実現しているのが最大の特徴です。
より詳しく知りたい情報があれば教えてください:
- メインCPUとMC68882がデータをやり取りするハードウェア(バス)の仕組み
- 68000(無印)などの古いCPUでMC68882を動かす際のエミュレーション(Fライン例外)の仕組み
- F-Lineオペコードの具体的なビット構成(マシン語の構造)
1. Fライン例外(エミュレーション)の仕組み
MC68020やMC68030といったメインCPUは、ハードウェアとしてコプロセッサ(FPU)インターフェースを内蔵しています。しかし、FPUが基板上に実装されていないシステムや、コプロセッサバスを持たない古いCPU(無印MC68000やMC68010など)でFライン命令を実行した場合、CPUは自動的にソフトウェアによるエミュレーションモードへ切り替わります。
この自動切り替えのトリガーとなるのが「Fライン例外(Line 1111 Emulator Exception)」です。
発生からエミュレーション実行までの流れ
- 命令のデコード
メインCPUはメモリから命令をフェッチした際、最上位4ビットが1111($F)であるコードを検出します。 - ハードウェアの応答確認
CPUはコプロセッサ(FPU)に対して、専用のバス(CPUスペース・サイクル)を使ってアクセスを試みます。FPUが基板に存在しない場合、システム(バス監視回路など)から「応答なし(バスエラーや、ハードウェア的な未実装シグナル)」が返されます。 - 例外処理の起動(トラップ)
FPUからの応答がないと判断したCPUは、即座に「Fライン例外」という割り込み処理を発生させます。 - ベクタテーブルの参照
CPUは、例外ベクタテーブルの ベクタ番号11(アドレス$0000002C) に書き込まれているアドレス(例外ハンドラ)へ強制的にジャンプします。 - ソフトウェア・エミュレータの実行
ジャンプ先には、OSやシステムが用意した「浮動小数点エミュレータ」のプログラムが配置されています。このプログラムが、実行しようとしたFライン命令(マシン語)をソフトウェアで解析し、CPUの一般レジスタを使って同様の浮動小数点演算を擬似的に計算(エミュレート)します。 - プログラムの復帰
計算が終わると、エミュレータはスタックに退避されていたプログラムカウンタ(PC)を進め、何事もなかったかのように元のプログラムの次の命令へと処理を戻します。
2. F-Lineオペコードの具体的なビット構成(マシン語の構造)
MC68881/MC68882の浮動小数点命令(Fライン命令)は、基本構造として「16ビット(1ワード)の基本命令コード」と、命令の種類に応じて後ろに続く「16ビット以上の拡張ワード(コマンドや即値データ)」で構成されています。
最も基本となる「一般演算命令(レジスタ間演算やメモリ転送など)」のマシン語ビット構成は以下の通りです。
① 基本命令ワード(第1ワード / 16ビット)
- ビット 15~12(Fライン識別子)
1111($F) 固定。これがFライン例外やコプロセッサ通信を起動するトリガーです。
- ビット 11~9(CpID:コプロセッサID)
- コプロセッサの識別番号。MC68881/MC68882の場合は通常
001が割り当てられます(システム内に複数のコプロセッサがある場合の識別用)。
- コプロセッサの識別番号。MC68881/MC68882の場合は通常
- ビット 8~6(タイプ)
- コプロセッサ命令のカテゴリを示します。一般演算や転送(
FMOVE,FADDなど)の場合は000になります。ちなみに条件分岐命令(FBcc)のときは010や011に変化します。
- コプロセッサ命令のカテゴリを示します。一般演算や転送(
- ビット 5~0(実効アドレス:Effective Address)
- メインCPU側のレジスタや、メモリアドレスの指定方法(アドレッシングモード)を格納します。
- ビット5~3:モード(データレジスタ直接、アドレスレジスタ間接、ポストインクリメントなど)
- ビット2~0:レジスタ番号(D0〜D7、A0〜A7)
② コプロセッサ・コマンドワード(第2ワード / 16ビット)
メインCPUが上記の第1ワードを検出すると、次にこの「コマンドワード」を読み取り、そのままFPUへ転送します。FPUはこの16ビットを見て「自分が何をすべきか」を理解します。
- ビット 15(予約):常に
0。 - ビット 14(R/Mビット)
0:ソース(演算元)がFPUの内部レジスタ(FP0〜FP7)である場合。1:ソースがメインメモリまたはメインCPUのレジスタである場合(データの型・サイズ指定が連動します)。
- ビット 13~10(Source:ソース指定)
- 演算元となるFPレジスタ番号、またはメモリ上のデータ形式(単精度、倍精度、拡張倍精度、整数など)を指定します。
- ビット 9~7(DestReg:転送先/演算先レジスタ)
- 結果を格納するFPU内部の浮動小数点レジスタ(FP0~FP7)を指定します。
- ビット 6~0(Extension:拡張オペレーションコード)
- 具体的な演算内容(命令の種類)を決定する最も重要な部分です。
- 例:
$00(FMOVE)、$22(FADD)、$28(FSUB)、$23(FMUL)、$04(FSQRT)、$0E(FSIN)など。
具体例:FADD.D (A0), FP2 の場合
(アドレスレジスタA0が指すメモリの倍精度浮動小数点を、FP2レジスタに加算する命令)
- 第1ワード:
1111 001 000 010000=$F210
(Fライン、CpID=1、一般命令、アドレスモード=A0間接010000) - 第2ワード:
0 1 101 010 0100010=$D522
(R/M=1メモリから、倍精度101、宛先FP2010、加算コマンド0100010)
このように、マシン語レベルではCPUとFPUが密接に連携できるよう、非常に整然としたビット配置になっています。
さらに理解を深めるために、以下のいずれかの詳細に進みますか?
- エミュレーション時の処理速度(ハードウェア実機との圧倒的な速度差について)
- メインCPUとFPUを繋ぐ物理的な信号線(バスサイクル)のやり取り
- 条件分岐命令(
FBcc)の際のマシン語コードの構造
Chapter 3 .




