レトロな(3) AltairZ80シミュレータ上のCP/MでDDT

Joseph Halfmoon

GDB(GNUのデバッガ)は今も活躍してますがそれは縁の下っす。プログラマの表に見える「煌びやかな」IDE環境の奥底でひっそりと動いておりますな。GDBレベルでデバッグする、というのは最近やらんな。今回使用してみるのはCP/M-80のデバッガDDTです。インテル8080用のデバッガっす。シンボリックですらないです。

※「レトロな」投稿順index

※Windows11上のWSL2上のUbuntu20.04LTS上のAltairZ80シミュレータ上のCP/M 2.2を懐かしく使わせていただいております。

※CP/Mは「今は亡き」デジタルリサーチ社のオペレーティングシステムです(ぶっちゃけMS-DOS以前のレトロな世界で一世を風靡したOSっす。)今は亡きティム様の以下の偉業によって旧デジタルリサーチ社の資産をやましからずに動かしてみることができるようになっております。感謝。

Tim Olmstead Memorial Digital Research CP/M Library

DDT(Dynamic Debugging Tool)

最近のよゐこは「知らない」と思われますが、DDTという殺虫剤が第2次世界大戦後の世界で大量に使われておりました。「戦後のドサクサ」の時期のフィルムなどをみると「進駐軍」が日本の子供に白い粉(DDT)を吹きかけてノミ、シラミなどを「撲滅」しておる姿が記録されていたりもします。殺虫剤といえばDDTという時代が確かにあったみたいデス。幸い、この年寄は進駐軍の時代から生きているわけではないので、DDTを直接吹きかけられた記憶はありません(もしかすると忘れているだけかも知れんけど。)なお、DDT、いろいろ安全性に懸念点あり、日本ではとっくに禁止されとります。

という背景知識があると、デバッガというものにDDTというお名前をつけた理由も想像できるというもの。こじつけかも知れんけど。プログラムの実行を途中で止めて、レジスタやメモリの内容を吟味したり、書き換えて様子をみたりすることができるもの。まさに8ビットのCP/Mの世界でDDTはデバッグのための光り輝くお道具だったわけであります。でも、近年のデバッガであれば漏れなくついてくる機能がありません。シンボリックデバッグです。近年のデバッガであれば、デバッグ対象のオブジェクトファイルの内部もしくは外部にシンボル・テーブルというものを持っていて、お名前(ラベル)でメモリ番地や値などを参照することが可能です。しかし、DDTにはそういう「高級な」機能はありません(だいたいメモリに余裕の無い8ビット機だったし。)16進数ではどこが何だか覚えきれんよな。プリンタにリスティングを出力しておいて横で赤ペンしながら追うのだね。ちなみにgccでデバッグビルドするときは -g をつけるとシンボルテーブルが生成されます。

そしてまたDDTでは高級言語のソースコードレベルでデバッグができるなどという機能はもとよりありません。CP/Mに漏れなく付属していたASM(インテル8080アセンブラ、これまたマクロアセンブラですらない)のためのデバッグツールです。さらにいえば論理番地、即物理番地の世界です。色即是空!違うか。

DDTをエクササイズするためのサンプルコード

前回作成したHello World.のプログラムを「チョイ変」してDDTを味わってみるためのプログラムに改変してみました。インテル8080のニーモニックですぞ。

; sample program for DDT
; J.Halfmoon September 16, 2023
BOOT    EQU 0000H
BDOS    EQU 0005H
PTEXT   EQU 09H
CR      EQU 0DH
LF      EQU 0AH
STKSIZ  EQU 128
        ORG 100H
START:
        LXI     SP, STACK
;
        LXI     H, NSUM
        MVI     M, 0
        MVI     C, 3
        XRA     A
LOOP:
        ADD     C
        DCR     C
        JNZ     LOOP
        MOV     M, A
;
        MVI     C, PTEXT
        LXI     D, HELLO
        CALL    BDOS
        JMP     BOOT

; Data Area
NSUM:   DB      5
HELLO:  DB      'HELLO WORLD.', CR, LF, '$'
        DS      STKSIZ*2
STACK:
        END

インテル8080には、A、B、C、D、E、H、Lという7本の8ビットレジスタが存在しますが、Mという名のレジスタはありません。しかし上記にMが登場するとき、HLレジスタに入っている16ビットデータをメモリアドレスとして解釈してメモリとやりとりすることになります。また、LXIというのはロード・ペア・イミディエイトという命令で、オペランドにHと書かれているとHLレジスタペアに第2オペランドの16ビット即値をロードする、という命令になります。

今回もLinux上のNanoエディタで編集しましたが、タブストップを「昔風」に8に設定したので前回のようにタブがガタガタしてません。多少の進歩(退化?)

DDTの練習用に真ん中辺にLOOPを設けてあり、3+2+1を計算してNSUM番地にお答えを格納するようにしてみました。ここをDDTでステップ実行しながら観察してみます。多分バグは入ってないと思うケド。

AltairZ80シミュレータ上での動作

まず、Linux上で記述したアセンブラソースをシミュレータ・ユニークな外界からファイルを読み出すRコマンドで取得します。ファイル名は気をつけて8文字.3文字の大文字です。それをアセンブルしておきます。assemble_HELLODDT

ASMアセンブラのエラーメッセージは、たった一文字なので、慣れないとなんだか分かりませぬ。今回はエラーなしみたい。よかった。

さてDDTを起動します。DDTは100h(0x100のこと)番地スタートのバイナリフォーマットであるCOM形式でも、ファイル内にメモリ番地が記述されているインテルHEX形式でもロードして実行可能です。今回はアセンブラ出力のHEXファイルをそのまま使ってます。

初期状態で0番地からスタートするようにPC(プログラムカウンタ)が設定されているので、XPコマンドで100(DDT内部では全て16進デス)番地に書き換えます。100h番地からLコマンドで逆アセンブルするとこんな感じ。DDT000

まず、最初にアセンブラのラベル NSUM番地(実際には011Ch番地)のデタラメな初期値5を念のためにクリアしているところまでステップ実行して確かめてみます。

dコマンドでメモリをダンプして値05hを確認。tコマンドでトレースしていきます。右側で次に実行される命令が逆アセンブル表示されてます。まずは赤の枠でSP(スタック・ポインタ)に22Ch番地をロード。続いてHLレジスタペアに11Ch番地をロード、そしてHLレジスタの指す先 11Ch番地に即値0を書き込みです(紫)。

DDT001

上記をみるとめでたく011Ch番地のメモリも書き換えできたみたいです。

次はループのところをトレースしてみます。たった3回の短いループですが回すとこんな感じ。注目すべきは下から4つ目のDCR C命令の次のところで左端のフラグがC0Z1M0E0I0となっているところです。Z(ゼロフラグ)が1ね。このお陰で次のJNZ命令はジャンプせずに下に落ちます。DDT002

ループで加算した結果、6もメモリにちゃんと書き込めているし。

レトロな(2) AltairZ80シミュレータ上のCP/MでHello World. へ戻る

レトロな(4) シミュレータ上のCP/Mでマクロアセンブラを へ進む