目次
前
次
CP/M80調査
Z80を利用したCP/Mマシンを使い、実験装置や
アームロボットを動かしていた学生時代のこと
を思い出し、今手元にあるZ80ボードでCP/Mを
動かすことにしました。
20世紀の8ビットOSの中で、CP/M80は大学の中では
頻繁に見かける、ありふれた環境でした。16ビット
のCUIを扱うOSで、MS-DOSがありふれた環境でした。
CP/80が動いていると、クロス環境でCP/M68kへの
移行がスムーズにできたのです。CP/M68kは企業
で開発したLSIのチェックに使っていました。
AVRにシリアルEEPROM、SDカードを接続して
FDC、仮想I/OでAVRを使い、Z80上でCP/Mを
動かしている外国のWebサイトを見ました。
英語、独語で書かれた内容を読むと、今ではCP/M80を
動かすのは、それほど難しくないと思います。
SDカードは、ホームセンターでワンコインで買え
シリアルEEPROMは、秋月電子で128kバイトが¥300
くらいです。これらをディスクの代わりに使えば
CP/M80を動かせるはずです。
本棚には、アスキー出版局の「入門CP/M」、「実習CP/M」、
「応用CP/M」がありました。約30年も前の本ですが、捨てず
にいたようで、本棚にPC8801のマニュアルと並んで、鎮座し
出番を待っていました。
この中の「応用CP/M」に、BIOSに関して詳細な説明が
ありました。DigitalResearch社が公開していたBIOS
の構造が書かれています。何度か読み直すと、次の
ことを思い出しました。
BIOSには、17種類の基本サブルーチンがある。
そのサブルーチンを、自分のシステムに合わせて
カスタマイズする。このBIOSだけは、自分で作成
しなければならない。
学生時代は、CP/M上のアプリケーションで実験装置や
ロボットを動かしたので、CP/Mを移植するところから
やってみることにします。
物置には、8インチドライブ、5.25インチドライブ
があり、メディアも新品で1箱ずつありました。
5.25インチディスクには、PC8801用CP/Mのシステム
ディスクが残っていたので、これを吸い上げて、EEPROM
やSDカードに移すと動かせそうです。
シリアルEEPROMやSDカードにシステムを移したとして
どうやってZ80のメモリ空間に、それらを転送するかを
考えると、ROMエミュレータでBootLoaderを入れれば
よいと思いつきました。
「応用CP/M」の内容を読んで、ディスクをどう扱うのかを
理解し直しました。
BIOSのJumpテーブルの後に、ディスクパラメータヘッダ
と呼ぶブロックを用意し、ここに記述されたパラメータ
に従って、ディスクドライブとやり取りします。
目的のプログラム、ファイルは、単なる情報の塊として
Z80のメモリ空間にロードあるいはセーブしますが、その
入出力に、このパラメータ類を使います。
ディスクパラメータヘッダを利用することで、ディスク
ドライブやメディアの違いを吸収して、DOS(Disk Operating
System)を実現しているのでした。
標準となるディスクは、8インチで次の諸元を持ちます。
- 1セクタ=128バイト
- 1トラック=26セクタ
- 1ディスク=77トラック
ディスクからデータを入出力するには、1セクタごとと
なりますが、使いにくいので、ディスクドライブが一度
に扱えるバイト数をブロックという単位で扱います。
1ブロックは、1024の(2のN乗)倍数で扱います。
ブロックを扱うために、ブロックシフト、ブロックマスク
と呼ぶパラメータを導入して、計算しやすくしています。
標準ディスクでないディスクメディアを使うときには
いろいろな変換をしなければならず、電卓を叩くのが
面倒なので、Tcl/TkでGUIアプリを作成しました。
1セクタの容量、扱うディレクトリ最大量等を指定するため
緑のボックスに数値を入力します。ボタンCalcをクリックする
と、黄のボックスに結果が出るようにしました。
このTcl/Tkのソースコードは、以下。
#!/usr/local/bin/wish
wm title . "parameter generator"
# initialize global values
set txtBPS 128
set txtSPT 26
set txtTTN 77
set txtSTN 2
set txtMDN 64
set txtBBS 1024
set txtBSN 0
set txtSPB 0
set txtBMS 0
set txtXSPT 0
set txtTBN 0
set txtALO "00000000 00000000"
set txtXALO "0 0"
#######################################
# define objects
#######################################
# label
label .lblBPS -text "Byte Per Sector"
label .lblBPSUnit -text "bps"
label .lblSPT -text "Sector Per Track"
label .lblSPTUnit -text "spt"
label .lblTTN -text "Total Track Number"
label .lblTTNUnit -text "count"
label .lblSTN -text "System Track Number"
label .lblSTNUnit -text "count"
label .lblMDN -text "Max Directory Number"
label .lblMDNUnit -text "count"
label .lblBBS -text "Block Byte Size"
label .lblBBSUnit -text "byte"
label .lblBSN -text "Block Shift Number"
label .lblBSNUnit -text "count"
label .lblSPB -text "Sector Per Block"
label .lblSPBUnit -text "count"
label .lblBMS -text "Block MaSk"
label .lblBMSUnit -text "count"
label .lblXSPT -text "Sector Per Track"
label .lblXSPTUnit -text "count(/128)"
label .lblTBN -text "Total Block Number"
label .lblTBNUnit -text "count"
label .lblALO -text "Allocation Table"
label .lblALOUnit -text "binary"
label .lblXALO -text "Allocation Table"
label .lblXALOUnit -text "count"
# entry
entry .edtBPS -textvariable txtBPS -bg green
entry .edtSPT -textvariable txtSPT -bg green
entry .edtTTN -textvariable txtTTN -bg green
entry .edtSTN -textvariable txtSTN -bg green
entry .edtMDN -textvariable txtMDN -bg green
entry .edtBBS -textvariable txtBBS -bg green
entry .edtBSN -textvariable txtBSN -bg yellow
entry .edtSPB -textvariable txtSPB -bg yellow
entry .edtBMS -textvariable txtBMS -bg yellow
entry .edtXSPT -textvariable txtXSPT -bg yellow
entry .edtTBN -textvariable txtTBN -bg yellow
entry .edtALO -textvariable txtALO -bg yellow
entry .edtXALO -textvariable txtXALO -bg yellow
# button
button .btnExit -text "Exit" -command "exit"
button .btnCalc -text "Calc" -command {
# computing block shift number
set tmp [expr $txtBBS % 1024]
if { $tmp > 0 } {
tk_messageBox -type ok -message "* illegal block byte size => $txtBBS *"
return
}
set txtBSN [expr int(log($txtBBS / 128) / log(2))]
# computing sector per block
set txtSPB [expr $txtBBS / $txtBPS]
# computing block mask
set txtBMS [expr int(pow(2,$txtBSN)) - 1]
# computing sector per track
set txtXSPT [expr $txtSPT * ($txtBPS / 128)]
# computing total block number
set tmp [expr ($txtTTN - $txtSTN) * $txtBPS * $txtSPT]
set tmp [expr $tmp / $txtBBS]
set txtTBN [expr $tmp - 1 ]
# computing allocation table
set tmp [expr ($txtMDN * 32) / $txtBBS]
set xtmp ""
set zero "0"
set one "1"
for {set i 0} {$i < 16} {incr i} {
# insert space
if { $i < $tmp } {
set xtmp "$xtmp$one"
if { $i == 7 } {
set xtmp "$xtmp "
}
} else {
set xtmp "$xtmp$zero"
if { $i == 7 } {
set xtmp "$xtmp "
}
}
}
set txtALO $xtmp
set tmp [lindex $xtmp 0]
set xtmp ""
for {set i 0} {$i < 8} {incr i} {
set xtmp "$xtmp [string range $tmp $i $i]"
}
set tmp 0
foreach e $xtmp {
set tmp [expr $tmp * 2 + $e]
}
# tk_messageBox -type ok -message " $tmp"
set txtXALO "$tmp 0"
}
#######################################
# window area placing
#######################################
grid .lblBPS -column 0 -row 0
grid .lblBPSUnit -column 2 -row 0
grid .lblSPT -column 0 -row 1
grid .lblSPTUnit -column 2 -row 1
grid .lblTTN -column 0 -row 2
grid .lblTTNUnit -column 2 -row 2
grid .lblSTN -column 0 -row 3
grid .lblSTNUnit -column 2 -row 3
grid .lblMDN -column 0 -row 4
grid .lblMDNUnit -column 2 -row 4
grid .lblBBS -column 0 -row 5
grid .lblBBSUnit -column 2 -row 5
grid .lblBSN -column 0 -row 6
grid .lblBSNUnit -column 2 -row 6
grid .lblSPB -column 0 -row 7
grid .lblSPBUnit -column 2 -row 7
grid .lblBMS -column 0 -row 8
grid .lblBMSUnit -column 2 -row 8
grid .lblXSPT -column 0 -row 9
grid .lblXSPTUnit -column 2 -row 9
grid .lblTBN -column 0 -row 10
grid .lblTBNUnit -column 2 -row 10
grid .lblALO -column 0 -row 11
grid .lblALOUnit -column 2 -row 11
grid .lblXALO -column 0 -row 12
grid .lblXALOUnit -column 2 -row 12
grid .edtBPS -column 1 -row 0
grid .edtSPT -column 1 -row 1
grid .edtTTN -column 1 -row 2
grid .edtSTN -column 1 -row 3
grid .edtMDN -column 1 -row 4
grid .edtBBS -column 1 -row 5
grid .edtBSN -column 1 -row 6
grid .edtSPB -column 1 -row 7
grid .edtBMS -column 1 -row 8
grid .edtXSPT -column 1 -row 9
grid .edtTBN -column 1 -row 10
grid .edtALO -column 1 -row 11
grid .edtXALO -column 1 -row 12
grid .btnCalc -column 4 -row 8
grid .btnExit -column 4 -row 9
このGUIアプリを作成することで、CP/M80はディスクを
次のように扱うことを理解できました。
データブロックは、ディレクトリを含んだファイルを
入れる領域です。CP/M80では、ディスクドライブAに
システムを入れたディスクメディアを入れ、リセット
するのがシステム起動のデフォルトです。
ディレクトリの中に、ファイルがどのブロックの
中に含まれているのかの情報を入れています。
16ビットのDOSの代表であるMS-DOSでは、FAT
(File Allocation Table)を利用し、ファイル
を入出力しますが、CP/M80ではFATを利用して
いません。
CP/M80がDOSの全盛時代には、FATを使うような
大きなディスクは、ハードディスクに限定され
フロッピーディスクベースのDOSでは、不要と
考えていたのでしょう。
シリアルEEPROMにCP/M80システムを入れるときは
FATを考える必要はないですが、SDカードを使うと
なると、FATの操作をしないとシステムのリロード
やファイル操作ができないことになります。
目次
前
次