補足 MZ80のROMルーチンで "SAVE" をしよう - データ形式を確認!
***
補足 MZ80のROMルーチンで "SAVE" をしよう - データ形式を確認!
***
SP5030の "SAVE" 命令文(92h)は、ROMのワークエリア 10F0h〜 にファイル名やバイト数などを設定して、ROMルーチンの 0021h と 0024h をコールしてカセットテープにプログラムを保存します。
ROMルーチンの LOAD の処理はエピ9で見ますので、SAVE の処理についても見ておきたいと思います。
テープに保存されるデータ形式が明確になるはずです。
***
ROMの 0021h と 0024h は、各々 0436h 番地と 0475h 番地へジャンプします。
0436h のルーチンでファイル名などの 128 バイトを保存して、0475h のルーチンでデータ本体を保存するのでしょう。LOAD の処理と同じですね。
コードを見ると、LOAD と類似していて、レジスタにパラメータを設定して共通のルーチンで処理するスタイルです。
0436h の設定: D=D7h, E=CCh, HL=10F0h, BC=0080h
0475h の設定: D=D7h, E=53h, HL=[1104h], BC=[1102h]
共通のルーチン 0444h の処理内容は、
・0733h をコール(チェックサムの計算)
・06B2h をコール(カセットのスタート検出)
・E==CCh なら "WRITING" + filename を表示
・07B8h をコール(ピー音の書込み)
・048Dh をコール(データの書込み)
・0563h へジャンプ(終了処理)
です。順番に見て行きます。
*
チェックサムは、[HL] からの BC バイトについて、各バイトの 1 の個数の合計です。
結果は 1197h 番地と 1199h 番地に転送します。1199h にも転送するのは、verify のときに参照するからです。
*
カセットのスタート検出は LOAD と同じルーチンで、カセットテープレコーダのモータの状態を調べて、停止中だったら "↓ RECORD. PLAY" と表示して、モータが回転するのを待ちます。
*
ピー音の書込みは、
E==CCh なら BC=55F0h, D=28h, E=28h
E!=CCh なら BC=2AF8h, D=14h, E=14h
と設定して、
・「0」の波形を BC 個書込み
・「1」の波形を D 個書込み
・「0」の波形を E 個書込み
・「1」の波形を1個書込み
します。55F0h とは 22000 です。2AF8h は 11000 です。波形の書込みの処理は後でね。
こんなに長いピー音は、...カセットテープの先頭には録音できない部分(リーダーテープ?)が数秒間ほどあるので(無いテープもあるけど)、間違ってプログラムの頭が切れることを避けるために長くしたとか。
...、でも録音するときにテープの頭出しをするのは常識だよね。音楽用のカセットデッキだと自動的に頭出しするから知らない方がいるのでは? と心配したのか。
*
データの書込みは、1バイトを書き込むルーチンがあってそれをコールします。
[HL] から BC バイト分を書込み、続けてチェックサム 1197h の2バイトを書込みした後、「1」の波形を1個書込みます。
それから「0」の波形を 256 個書込み、もう一度データとチェックサムと「1」を書込みます。
*
終了処理は LOAD と同じルーチンです。カセットテープレコーダのモータを停止します。
それと 119Ch 番地を参照して F0h だったら EI します。119Ch 番地は TIM ST で F0h にしてます。
***
**
*
07A5h 番地が1バイトを書き込むルーチンです。
データは A に入っていて、
push BC
ld B, 08h
call _0780h # 1
_07ABh:
rlca
call C, _0780h # 1
call NC, _0767h # 0
dec B
jp NZ, _07ABh
pop BC
です。0780h は「1」の波形を書込み、0767h は「0」を書込みます。
Z80の RLCA 命令は A の MSB を Cf にセットして、A を左ローテートします。つまり、Cf = A/128, A = A*2 + Cf です。
そうして Cf により「1」か「0」のどちらかのルーチンをコールするのです。Z80の条件付きコール命令を旨く使ってます。
A の MSB が Cf に入りますから、ビットの順番は MSB First になります。うんそうだったよね。
この処理をみる限り、最初に「1」の波形を書込み、続いて8ビット分の波形を書込みしています。LOAD の処理とは違う感じ。最初の「1」はどこ行った?
*
最初の「1」の波形を読み落としているのかと、...、LOAD の処理を再度確認したら、...。
0510h のルーチンでは、まず波形を読んで「0」だったら読み捨てます。ここで最初の「1」が読み込まれるようです。
0624h の8ビットの読取りでは8波形を読んで、最後に次のバイトの最初の「1」を読む。なっとくー
*
0767h 番地のルーチンで「0」の波形を、0780h 番地のルーチンで「1」の波形を書込みます。
カセットテープレコーダへの出力は E003h 番地で制御します。03h を転送すると出力が "1" となり、02h を転送すると出力が "0" になります()。
「0」の波形は、
push AF
ld A, 03h
ld (E003h),A # --- "1" を出力
call _0759h
call _0759h
ld A, 02h
ld (E003h),A # --- "0" を出力
call _0759h
call _0759h
pop AF
ret
です。0759h のルーチンは時間待ちです。LOAD では 0760h のルーチンを使いましたが、それよりループ回数が1だけ多いルーチンです。
「1」の波形は 0759h を4回コールして時間調整をします。
0759h のルーチンの処理時間は、CALL 命令 17 ステート、LD 命令 7 ステート、DEC 命令 4 と JP 命令 10 の計 14 ステートを 14 回ループ、RET 命令 10 ステートで、17+7+14*14+10 = 230 ステートです。
***
これで処理の内容は一通り分かりましたね。
***
**
*
波形がどうなるか確認しましょう。
ld (E003h),A の後にカセットテープへの出力が変化する遅延時間は、"1" になる場合と "0" になる場合とは同じだと仮定します(...、違うことも多いけどさ)。
...、出力が "1" である時間は、0759h の 230 ステートが2回(or4回)と、LD 命令が 7 ステートのと 13 ステートが各1個。よって 230*2+7+13 = 480 ステートか、230*4+7+13 = 940 ステート。時間換算すると 240 μs か 470 μs ですね。
...、"0" の時間は、...あれ? 最低でも、... 0759h が2回(or4回)と POP, RET,... , CALL, PUSH, LD, LD は入るので 230*2+10+10+ ... +11+17+13 = 521 ステートか、981 ステートですけど。...、その間に別の命令が入るからもっと長くなるかな。
07A5h のルーチンで1バイトを書き込むときだと、...
最初の「1」の次が「1」なら RLCA の 4 ステート増える。
最初の「1」の次が「0」なら 4+10 = 14 ステート増。
「0」の連続のときは DEC, JP, RLCA, CALL(フラグ偽) が入って、6+10+4+10 = 30 ステート増。
「1」の連続も同じく 30 ステート増。
「0」→「1」なら DEC, JP, RLCA だから 20 ステート増。
「1」→「0」なら 40 ステート増。
つまり、"1" は 480 ステートで固定なのに対して、"0" は 525 〜 561 ステートでバラつく。むむむ。合っているのかな?
1バイト書込みの後は Break-key の検出をしていて更に長くなります。LD, AND, JP, INC, DEC, LD, OR, JP, LD, PUSH, LD で、13+7+10+6+6+4+4+10+7+11+7 = 85 ステート。...、本当かな?
これは最初の遅延時間の仮定が違うのかも。...、多分違うのだろう。でも。もしかしたら。"1" の時間が「0」= 240μs で「1」= 470μs なので、その中間 (240+470)/2 = 355μs で波形を判定した後は、次の "1" の立ち上がりを検出できれば良いから、..."0" の時間は気にしていない可能性も?
***
**
*
カセットテープに書込む情報を整理します。
・最初のピー音「0」を 22000 個
・「1」を 40 個、「0」を 40 個、「1」を 1 個
・ファイル名など(128 バイト)、チェックサム(2 バイト)
・「1」を1個、「0」を 256 個
・ファイル名など(128 バイト)、チェックサム(2 バイト)
・「1」を1個
・2回目のピー音「0」を 11000 個
・「1」を 20 個、「0」を 20 個、「1」を 1 個
・データ本体([1102h] バイト)、チェックサム(2 バイト)
・「1」を1個、「0」を 256 個
・データ本体([1102h] バイト)、チェックサム(2 バイト)
・「1」を1個
バイトの書込みは「1」が1個と「1or0」が8個です。
*
LOAD の処理と比べてみると。
・065Eh のルーチンは「0」の波形は読み捨てで、40 個の「1」と 40 個の「0」をカウントしたら、次の0⇒1検出でリターンします。
・続く 0510h のルーチンでは、まず波形を読んで「0」だったら読み捨てて、「1」だったらデータの読込みを開始します。0624h のルーチンを繰り返しコールします。
・0624h のルーチンは8ビット分の波形を読み、次の0⇒1検出でリターンです。
OK?
***
**
*
...。
***
**
*
ROMルーチンによる SAVE の処理を解読しました。
ピー音の最後は「1」で、バイトの最初、...スタートビット?、...も「1」なのは、そう処理しているからですね。
チェックサムの後に「1」が1個あって「0」が 256 個続くのはその通りでした。8ビット単位で見ていたら1ビットのズレがあって気になっていたのですが、「1」を入れているならそうなりますね。
SP5030のテープでは最初のピー音は 22000 個は無いけど。長いからカットしたのかな。ロードで支障がでないなら短い方が良いね。22000 個だと10秒くらいあるから。
シリアル通信では、アイドル中は「1」(マーク)で、スタートビット「0」(スペース)からデータが始まるのが多いのですが、...逆ですね。波形の「0」と「1」と、ビットの0と1の対応をどうするかは設計次第ですけど。ビットの順番についてもシリアル通信なら LSB First が多いのですが、MSB First で処理しているのですね。これも設計次第ですけど。ですけど。
1波形の時間は、1バイトの書込み中ですら、502.5μS から 520.5μS の範囲でバラつくようです。±2%のジッタが出ている? 確認するには実機が必要ですが、...ジッタの分析をしたいよう。
***
MZ80はそうなんだ、と確認しました。
***
間違いの指摘とか疑問とか、ご意見・ご感想とかありましたら、どうぞ感想欄に!
***
2026.4.18 エピ順番を入れ換え(初出: 2026/03/29 03:14)
2026.4.19 エピタイ修正




