2019年11月4日月曜日

ATmega164PのJTAGを禁止する

初期状態のATmega164PのPC2 - PC5 は、JTAGに割り当てられているため、そのままでは入出力に使えません。これらのピンを入出力で使うには、JTAGを禁止する必要があります。
ネット上ではソフト的に禁止する方法が見つかるのですが、試したてみたものの、うまく行きませんでした。

ハード的にJTAGを禁止するには、ヒューズビットの上位バイトのbit6を1に書き換えます。
私の手持ちの石の上位バイトは0x99でしたので、これを0xD9に書き換えます。
AVR ISP mkIIを使ってavrdudeで書き換える場合だと、
avrdude -c avrisp2 -P usb -p m164p -U hfuse:w:0xD9:m 

ヒューズビットの設定を間違えると最悪起動しなくなることもあるので、注意が必要です。

2019年10月23日水曜日

Z8k-COFF フォーマット 3

リロケーション情報

Z8k-COFFで拡張され、offsetとstuffが追加されている。
変数名はBFDのコードに合わせたが、用途は不明。
リロケーション情報は、r_vaddrが昇順になるよう並べないと、リンカがsegmentation fault を起こす。

 r_vaddr  (4 bytes)
  リロケーションを行う箇所のセクション先頭からのアドレス。

 r_symndx (4 bytes)
 シンボル番号


 offset (4 bytes)
 オフセット+シンボル中の e_value が、リロケーションを行う箇所の値になる。

 r_type (2 bytes)
 アドレスのタイプ。確認できているのは次の6種類。
 外部シンボルをPC相対で参照するケースがあるのか分からないが、
 アセンブリ言語でコードを書いてobjdumpで確認すると、これらのタイプが使われている。
 内部シンボルへのPC相対アドレスは解決できるので、リロケーション情報にはない。 
 16bitの短縮セグメントアドレスがあるかは不明。

 0x01 - 16bit オフセットアドレス (ノンセグメント)
 0x02 - 8bit PC相対 ( JR )
 0x04 - 16bit PC相対 (LDA, LDAR)
 0x05 - 12bit PC相対 ( CALR )
 0x11 - 32bit セグメントアドレス
 0x25 - 7bit PC相対 ( DJNZ )

 セクションデータ中のr_vaddrが指すアドレスには、各r_typeごとに次の値が書き込まれている。 
 
 0x01 : セクション先頭からの16 bitアドレス 
 0x02 : セクション先頭を指すPC相対アドレス (v_addrが指す次のアドレスが基点)
 0x04 : 0x0000が書かれている。
 0x05 : セクション先頭を指すPC相対アドレス (v_addrが指すアドレスが基点)
 0x11 : セクション先頭からのセグメント形式でない32bitアドレス
 0x25 : 何を指しているのか不明 

 stuff (2 bytes)
 不明 0x5343 "SC" が書かれている。

XOUTとCOFFの両フォーマットがなんとなく理解できたので、これで先に進めます。
まずはシンボルテーブルあたりから変換できそうか試してみるつもりです。

Z8k-COFF フォーマット 2

シンボルテーブル

シンボルが定義されたテーブル。
シンボル値は、シンボルクラスによって意味が変わったりするので複雑。

 e.e_name 
 シンボルネーム。8文字に満たない場合は、残りを0で埋められている。
 8文字の場合は、0で終端していない。
 先頭の4byteが0の場合、次の4byteは文字列テーブルへのオフセットになっている。

 e_value
 シンボルの値。e_scnume_typeによって意味が変わる。

 e_scnum
 シンボルが属しているセクション番号。セクションテーブルの先頭セクションは1になるので注意。
 0以下の値は特別の意味を持つ

  0 : 外部シンボル
 -1 : 即値 アセンブラで .equ 定義した値など、再配置で変化しない値
 -2 :  デバッギングシンボル

 e_type
 シンボルの型を表すことになっているが、オブジェクトファイルを見ると何故かいつも0x0000。
 実行ファイルには、何か値が入っている。
 e_sclass
 シンボルクラス。 DJGPP COFF SPEC の Symbol Table を参照 

 e_numaux
 現在のエントリに続く、補助エントリの数。シンボルクラスに応じて補助エントリの内容が変わる。

補助エントリ
 セクションクラスがファイル名とセクション定義の補助エントリは確認されるが、その他は不明。
 逆アセのスス乂の5.5.5 補助形式5:セクション定義を参照。

文字列テーブル

シンボルテーブルの後に続く、0で終端された文字列の配列。長いシンボル名などが収められる。
先頭4byteは、このテーブルのサイズ(先頭4byteを含む)。

2019年10月22日火曜日

Z8k-COFF フォーマット 1

つづいてZ8k-COFF

COFFの資料はネット上にいくらでも転がっているので、理解は楽だろうと踏んでいたのですが、x86のものや、WidowsのPEフォーマットのものばかりで、Z8000のCPU依存に関する情報はほとんどありません。
それでも基本構造は同じなので、機種依存はGNUのBFDライブラリのソースコードを参照しながら進めています。

参考にした情報のリンクを上げておきます。
 DJGPP COFF Spec
 逆アセのスス乂
 BFDソースコード

gccとasでテスト用のオブジェクトファイルを作り、バイナリエディタとobjdumpで変化を確認していく地道な作業を続けており、Z8000依存で判明した部分や忘れてしまいそうな事などをメモ代わりに書いていきます。変数名は、DJGPPのドキュメントに合わせてあります。

Z8k-COFF フォーマット

次の7パートからなっている。
 ・ ファイルヘッダ
 ・ オプショナルヘッダ
 ・ セクションテーブル
 ・ セクションデータ
 ・ リロケーション情報
 ・ シンボルテーブル
 ・ 文字列テーブル 

ファイルヘッダ

ファイルの全体的な構造が記されている

 f_magic 
 0x8000 - Z8001/2の識別はflagsによる。

 f_flags
 0x1000 - Z8001 セグメンテッドコード
 0x2000 - Z8002 非セグメンテッドコード 
 0x0200 - 不明 (ビッグエンディアンかも)
 0x0008 - ローカルシンボル情報なし 
 0x0004 - ライン番号情報なし
 0x0002 - 実行形式
 0x0001 - リロケーション情報なし

オプショナルヘッダ

実行ファイルの場合のみ付き、実行ファイルに必要な付加情報が入っている。
下の2つは詳細不明。

 magic
 0x0000 

 vstamp
 0x0000 

セクションテーブル

セクションの情報が記されたセクションヘッダの配列。

 s_flags
 0x80 - 初期地を持たないデータ(BSS)
 0x40 - 初期値をもつデータ
 0x20 - 実行コード
 

2019年10月19日土曜日

XOUTフォーマット 4

つづき

シンボルテーブル 12 byte

参照されるシンボルの情報(x_sym) の配列。

 x_sy_sg (1 byte)

 シンボルが含まれるセグメント番号(セグメント情報のインデックス)
 255の場合は、即値か外部参照を示している。

 x_sy_fl (1byte)

 シンボルのタイプ

 1 - ローカルシンボル (デバッグ用)
 2 - 未定義の外部シンボル (参照先が外部)
 3 - グルーバルシンボル (外部から参照可能なシンボル)
 4 - セグメント名

 x_sy_val (2 byte)
 
 シンボル値。セグメント番号とタイプによって、値の意味が変わる。
 
    セグメント番号 : 0-127, タイプが1, 3, 4の場合は、セグメント内でのオフセットアドレス
 セグメント番号 : 255, タイプ : 1 の場合は、16bit即値
 セグメント番号 : 255, タイプ : 2, 値 : 0 の場合は外部シンボル
 セグメント番号 : 255, タイプ : 2, 値 : 0 以外の場合は、BSSに確保するメモリースペース
 

 x_sy_name (8 byte)

 8文字のシンボル名
 8文字に満たない場合は、0で埋められる。8文字の場合は、0で終端されないので注意。

補足
コンスタントプール内のシンボルはローカルで、必ずしもシンボルテーブルに登録されない。
引数の文字列などはラベルがないので、リロケーション情報があるだけ。

セグメント番号がBSSを指すのは、明示的にBSSセクションを指定した場合のみらしい。
初期値のないグローバル変数は、セグメント番号 : 255, タイプ : 2 になっており、
複数のオブジェクトで定義されている場合は、その中から最大値がBSSに確保される。

XOUTフォーマット 3

つづき

再配置情報 6 byte

コードとデータ中にあるアドレス参照している箇所の情報 (x_rel) の配列。
リンカは、コードが配置されるアドレスとシンボルテーブルをもとに、そのアドレスを書き換える。

 x_rl_sgn (1 byte)

 アドレス参照箇所が含まれるセグメント番号 
 Z8001のセグメントではなく、セグメント情報のインデックスなので注意

 x_rl_flg (1 byte)

 アドレスの形式

 1 - 16 bit アドレス 
 2 - 16 bit ショート型アドレス 
 3 - 32 bit セグメント + オフセットのアドレス
 5 - 外部参照 16 bit アドレス 
 6 - 外部参照 16 bit ショート型アドレス
 7 - 外部参照 32 bit セグメント + オフセットのアドレス

 x_rl_loc (2 byte)

 アドレス参照している箇所
 セグメントの先頭からのオフセットになっている。

 x_rl_bas (2 byte) 

 ローカル参照の場合は、参照先があるセグメント番号
 外部参照の場合は、シンボルテーブルへのインデックス

ローカル参照の場合は、参照先があるセグメントの先頭アドレスと、参照箇所に書かれている値を足した値がアドレスになる。シンボルがローカルな場合はシンボルテーブルに存在しない。

XOUTフォーマット 2

つづき

セグメント情報  4 byte 

セグメントとなっているが、Z8001のセグメントとは直接関係していない。
COFFやELFのセクションに当たる。セクションをセグメントに対応させれば同じことなので、
完全に別物というわけでは無いのが紛らわしいところ。

ファイル中に含まれるセグメントの数だけ、次の構造 (x_sg) が並べられ配列になっている。

 x_sg_no (1 byte)

 セグメント番号 0 - 127か255が入る。
 Z80001のセグメントに対応していそうだが、ノンセグメンテッドのコマンドをみても、0 - 3の
 値が割り当てられており、ただの通し番号かも。 
 
 255の場合は、リンカがセグメント番号を割り当てることを示す。
 リンク可能なファイルには、この値が入っている。
 
 x_sg_type (1 byte)
 
 セグメントに含まれるデータのタイプ

 1 - 初期化されないデータ (BSS)  
 2 - スタック
 3 - 実行コード
 4 - 定数  
 5 - 初期値を持つデータ
 6 - コードとデータは混合 保護
 7 - コードとデータは混合 非保護 

 1,2は、ファイルには含まれず、メモリー上にロードされた時点で、領域が確保される。

 3は、実行コード本体。

 4は、関数への引数で渡される文字列など変更されないデータ。

 5は、初期値を持つグローバル変数や文字列。

 6,7はROMへの書き込み用らしい。

 x_sg_len (2 byte : unsigned)
 
 セグメントのサイズ

コード/データ


 ここには、命令コード、定数、初期値を持つデータが、セグメント情報の順番にまとめられている。
 各セグメントの開始位置は、ヘッダ情報から求まる。
 初期化されないデータ (BSS) とスタックは、ここに含まれない。

2019年10月14日月曜日

XOUTフォーマット 1

XOUTはCP/M-8000で使われている、実行とリンクが可能なオブジェクトファイルの形式です。
解析しているうちに頭がこんがらがってきたので、ファイル構造をメモ代わりに書いていきます。
参考にした情報は、CP/M-8000 Programmer's Guide (注 PDF)と P-CP/M-Z8k SOURCES (注 ZIP) にあるLD8Kリンカのソースコードです。

説明中の変数名は、リンカのソースコードで使われている名前をそのまま使っています。
ちょっとわかりにくい命名ですが、コードを読むときの助けになるよう、そのままにしておきます。

Z8000はビッグエンディアンなのでデータは最上位バイトから最下位バイトへの並びです。
各データ構造は2 byte境界で揃えられています。

XOUTフォーマット  

a.outフォーマットと似た構造になっており、5つのパートからなっている。
  1. ヘッダ
  2. セグメント情報
  3. コード/データ 
  4. 再配置情報
  5. シンボルテーブル

ヘッダ 16 byte

ここには、セグメントの数や各パートのサイズなどのファイルの全体的な構造が書かれている。

 x_magic (2 byte) 
 
 ファイルの形式を認識するための番号
 
 0xEE00 セグメンテッド    実行不可      
 0xEE01 セグメンテッド    実行可能
 0xEE02 ノンセグメンテッド 実行不可 
 0xEE03 ノンセグメンテッド 実行可能  命令コードとデータの空間は非共有
 0xEE07 ノンセグメンテッド 実行可能  命令コードとデータの空間は共有
 0xEE0B ノンセグメンテッド 実行可能  命令コードとデータの空間を分離 

 リンク可能なのは0xEE00か0xEE02で、セグメンテッドとノンセグメンテッドはリンクできない。
 リンカのソースでは、リンク可能な0xEE06と0xEE0Aも存在していることになっている。
 配布されているCP/Mのイメージに含まれるコマンドは、0xEE03か0xEE0Bだけで、
 0xEE01, 0xEE07は見当たらない。

 0xEE03と0xEE07の違いは不明だが、64k byteの空間に命令コードとデータを収める形式。
 マジックが0xEE07のコマンドが無いため、比較ができないので不明。
 
 0xEE0Bは、コードとデータの空間を分離して、64k byteづつの空間をそれぞれに割り当てる。
 1セグメントが64k byte の制約を緩和できる。CPUに外付けのMMUがないと実現できない。


 x_nseg (2 byte) 
 
 セグメントの数
 
 x_init (4 byte) 
 
 コード/データのバイト数

 x_reloc (4 byte) 
 
 再配置情報のバイト数
 値が0の場合は、ファイルはリンクされている。

 x_symb (4 byte)
 
 シンボルテーブルのバイト数
 値が0の場合は、ファイルからシンボルテーブルは取り除かれている。

2019年10月12日土曜日

CP/M-8000は移植できるのか リンカ 4

先週の段階では、COFF を CP/M-8000 の XOUT に変換して、CPMLDR.SYS をリンクしようとしていたが、ふと思い立って方針を変更することにした。

もし、XOUT を COFF に変換できれば、GNU Binutils の ld を使ってリンクできるし、 COFF オブジェクを XOUTに変換し直せれば、CP/Mの実行ファイルが得られるはず。

COFF to XOUT は比較的簡単そうな気がするが、逆の XOUT to COFF がややこしそう。リンカを移植した時、動かすことを優先して XOUTのフォーマットをあまり理解せずガシガシ進めたので、XOUTの解説とリンカのソースコードを頼りに試行錯誤することにする。

移植したリンカは権利が不明なので、公開できないが、これだと変換ツールのソースコードの公開が可能になる。、Linux上で作業できれば、CP/M-8000 が動く実機が無くても、物好きな人がCP/Mの移植に挑戦できるようになる。

リンカを移植するのに使った時間は無駄になるが、しょせんは趣味なので気にしないし、人生の暇つぶしににはもってこい。

ついでに。
リンカを移植していたとき、CPMLDR.REL と CPM.LIB をリンクしようとすると、Segmentation Fault が出て困っていたが原因がわかった。CPMLDR.RELの一部が壊れている。


0x251C からの 0xFF 0xFF の2 byte を 0x00 0x00 に書き換えればOK。おそらく、その前に10箇所ある 0x08 0xB6 も 0x00 0x00 に書き換えた方が良いと思うが、これは実害は無いので、そのままでも大丈夫。
 
今日、外は台風でえらいことになってる。これを書いている時点では、台風は未だ上陸はしていない。これから記憶に残る1日になるんやろか。

2019年10月5日土曜日

CP/M-8000は移植できるのか リンカ 3

とりあえずCP/M-8000のリンカ ld8kは、Linux上に移植し動作するようになりました。
だれの役にも立たないでしょうが、移植のコツみたいなものを書き出しておきます。

もともとのC言語のソースコードはスタイルが古典すぎて、そのままではGCCでコンパイルできないので、まず今風に書き換える必要があります。
使われているコードのスタイルは次のようになっています。
func(a, b) int a;
{
    return(a + b);
}
  • 引数の型宣言は、関数の後ろ
  • 引数の型宣言は省略できる。型は int になる
  • 戻り値の宣言は省略できる。型は int になる
  • int のサイズは16bit
  結構ルーズな書き方も許されていて面食らうこともあります。
 
標準ライブラリも今時の標準と違うので、前後のコードと予想した動作から別のものに書き換える必要があります。
  • opena() --- テキストモードでファイルをオープン
  • openb() --- バイナリモードでファイルをオープン
  • seek() ------ ファイルシーク  
このあたりは、fopen(), fseek() で置き換えました。
 ファイルをオープンするモードや、シークポジションの基点などは、コードから推測して指定します。

Z8000がビッグエンディアンなので、スモールエンディアンと相互に変換するようコードに書き換えていく必要があります。これは、オブジェクトファイルから読みだす時と書き出す時に必要になります。
例えば、
unsigned int a;
read(infile, &a, 2);
上のような、ファイルから16bit値を変数 a にロードするコードは、下のように書き直します。
unsigned int a;
unsigned char buf[2];

fread(buf, 2, 1, infile);
a = buf[0] * 256 + buf[1];

CP/M-8000のCコンパイラは、関数名や変数名に8文字までしか使えないようで、命名が短縮され過ぎています。当時、短い名前は当たり前だったのでしょうが、名前から役割を推測しづらく、読み解くのは結構きついです。

なんやかんやでリンカは移植できたのですが、もとのCP/Mのライセンスの扱いがよくわからないので、コードは公開できないでしょう。 コードも分かりづらいし公開できるよう、できれば完全に書き直したいのですが、とりあえず先に進みます。

次の段階は、BIOSを書くことです。参考になるOlivetti M20用のBIOSのソースコードがあるのですが、アセンブリ言語とC言語で書かれており、CP/M-8000が動く環境がないとリビルドできません。なので、クロス開発することになるのですが、残念ながら XOUT フォーマットを吐いてくれる開発ツールは存在しないようです。GNU Binutils のクロスアセンブラは COFF を吐いてくれるので、これを XOUT に変換してリンクすることにします。

ということで、次はオブジェクトファイルの変換ツールを作ることが目標になります。

先ながいなぁ…

2019年9月22日日曜日

CP/M-8000は移植できるのか リンカ 2

libcpm.a をリンクする必要があるので、こちらのフォーマットも調べておきます。ar8kのソースコードが P-CP/M-Z8k SOURCES に、コマンドの説明はProgrammer's Guide に存在していました。

構造は単純です。先頭の2byteがマジックナンバで、ar ヘッダーと x.out フォーマットのオブジェクトがその後に続いてゆくだけです。先頭から手繰っていけば、すべてのオブジェクトが読んでいけます。

CP/M-8000では、ブートセクタに書き込まれたローダがCPMLDRを実行し、ファイルシステム上にあるCPM.SYSを読み込み実行する2段階の起動方法になっています。
The Unofficial CP/M Web Site で公開されているファイルでは、Olivetti M20用のCPMLDRが、CPMLDER.REL とLIBCPM.A をリンクするとできるようなので、当面は、これらをリンクできるようになることを目標とします。


2019年7月31日水曜日

CP/M-8000は移植できるのか リンカ 1

CP/M-8000のリンカ ld8kのソースコードがP-CP/M-Z8k SOURCESの中に見つかったので、Programmer's Guideと共に読み進めています。

最初、ソースコードを修正してLinuxで動くようにしようとしたのですが、文法が古典過ぎて最近のコンパイラは通りません。また、バイトオーダの違いもあって、素直には動かせそうにないので、ソースコードを参考に書き直すことにしました。

ソースコードだけからでは意味がわからなかった部分も、Programmer's Guideにコマンドファイルフォーマットとリンカの使用方法の説明があったおかげで、再配置データやシンボルテーブルが読み出せるようになりました。

下はcpmsys.relのシンボルテーブルを読み出した例。


このファイル中のシンボルは、225個あることがわかります。どうやら、セグメント番号0xffでタイプ2のシンボルが外部定義シンボルのようです。

リンクの仕方は、ソースコード読み進めてこれから理解していきます。

2019年7月28日日曜日

CP/M-8000は移植できるのか CPMLDRを探る 1

The Unofficial CP/M Web site にある CP/M-8000 1.1 で確認を始めています。
CPMLDRのソースコードは、disk4 に収まっています。

BIOSは、Olibetti M20 というマシン用に書かれています。幸いなことに、ネット上にはマシンのハードウェア情報があるので参考になりそうです。

bios.sub がBIOSをビルドするためのサブミットファイルです。そこから読み進めると、コンパイルとアセンブルが必要なファイルは、
  • bios.c
  • biosasm.8kn
  • biosdefs.8kn
  • biosboot.8kn
  • biosif.8kn
  • biosio.8kn
  • biosmem.8kn
  • biostrap.8kn
  • syscall.8kn
  • fpedep.8kn
  • fpe.8kn
  • libcpm.a
biosboot.8knの中に、メモリ配置に関する情報がありました。
  • ブート時のシステムの配置アドレスは、<<10>>0000
  • ブート時のシステムのPSAは、<<4>>0100
  • システムの配置アドレスは、<<11>>0000
  • システムのPSAは、<<2>>0100

BIOSはアセンブリ言語だけで記述されていると思っていたら、C言語も使われています。結構面倒なことになってきました。gccが使えるのか、もしくはアセンブリ言語で書き直しかな。

fpedep とfpe はソースが無く、アセンブルされたオブジェクトのみがあります。これらは、浮動小数点演算用のようで、ローダに必要なのかわかりません。

CP/M-8000は移植できるのか

正直、かなり手強そうです。

現在、The Unofficial CP/M Web Site にある、下の2つのドキュメントを読んでいるところです。

・ Programmer's Guide
・ System Guide

ドキュメントが英語で書かれているのは仕方ないとして、私がCP/Mの知識をほとんど持っていないことが、理解が進まない原因です。さらに、セグメントやシステム/ユーザーモードの存在とかが話をややこしくしていて、頭がだいぶ混乱しています。しかし所詮は趣味なので、最高の人生の暇つぶしとして移植を楽しんでいくつもりです。

これからは、情報をまとめるためのメモ代わりとして、ここを使っていきます。

とりあえずわかったこと、
  • cpm.sysがOS本体で、bios.rel, cpmsys.rel,  libcpm.a をld8kでリンクして作る 。
  • cpmsys.rel は、ccp.rel, bdos.rel, libcpm.a をリンクしたもの。
  • 簡略版のBDOS と BIOSをリンクしたcpmldrが、cpm.sysをメモリーにロードする。
  • CP/Mはシステムメモリ空間、トランジェントコマンドはノーマルメモリ空間を使う(ハードウェアが対応していれば)
  • ノンセグメントのコマンドでは、インストラクションとデータのメモリ空間をわけることもできる。(ハードウェアが対応していれば)
未だ不明なこと
  • cpm.sysが読み込まれるメモリアドレスがどう決まるのか?
  • TPAのアドレスがどうやって決まるのか?
  • トランジェントコマンドは、何かリロケータブルなフォーマットなのか?
  • ccp.rel と bdos.rel は提供されていない?

CP/M-8000の実行環境が無いとld8kが使えないので、cpm.sysを作るには、クロスでリンクできる環境が必要そうです。

とりあえず、CP/M本体をメモリーにロードするcpmldrを動かすことを当面の目標にします。

2019年7月26日金曜日

z8kmon公開

Z8001用の機械語モニターをGitHubにさらしました。
なんの使いみちの無いようなものでも、誰かの役に立てるかもしれず。

https://github.com/4sun5bu/z8kmon


気が向いたらアップデートしていきます。
使い方はREADMEを読めば、大方わかるはずです。
<<0>>0000からRAMが配置されている不自然なメモリー配置が前提になっているので、ROMを先頭に配置する方は、適当にリンカスクリプトを書き換えてください。

これからは、作ったZ8001ボードでCP/M-8000が動かせるのか検討する、セカンドフェーズに入っていきます。

2019年7月14日日曜日

Z8001クロス開発環境 binutilsをビルドしてみる

以前、導入したクロス開発環境だと、binutilsのバージョンが2.19と古いので、新しいバージョンをビルドします。最新は2.32で、Z8000も未だにサポートされています。

ビルドに使った環境は、Ubuntu 18.04.2 LTS (Bionic Beaver) 64bit です。

 https://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.gz

ダウンロードしたら、展開します。

    tar xvzf binutils-2.32.tar.gz

ビルド用のディレクトリを作ります。

    mkdir cross-dev

ビルド用のディレクトリに入り、configureを実行します。targetには、z8k-elfは指定できないようなので、z8k-coffにします。prefixには、インストールするディレクトリを指定します。

    cd cross-dev
    ../binutils-2.32/configure --target=z8k-coff --prefix=$HOME/opt

そして、

    make
    make install

私の環境では、エラーもなく結構かんたんにビルドできました。
prefixで指定したディレクトリのbinの下に、実行ファイルが入っていますので、PATHを通しておきます。

残念ですが、GCCは公式にはZ8000をサポートしていないようなので、古いバージョンを使うしかないですね。



2019年7月4日木曜日

Z8001のアセンブリ言語 その2

また誰の役にも立たないと思いますが、Z8001のアセンブリ言語でコードを書いているときに気づいたことを書き留めておきます。

レジスタの値がゼロか正負かを調べるには TEST より OR 

レジスタの値がゼロかどうか調べたり、正か負を調べる場合に、TESTを使うと7クロックですが、同じレジスタ同士でORを使うと4クロックですみます。TESTは、レジスタではなくメモリの値を調べる場合に使ったほうが、メモリから一度レジスタに読み出す必要がないのと、レジスタを破壊しなくてすむので有利になります。またTESTは32bit値が使えますが、ORでは使えません。

レジスタのゼロクリアには CLR より XOR

レジスタの値をゼロにしたい場合、CLRを使うと7クロックですが、同じレジスタ同士でXORを使うと4クロックですみます。ただし、CLRではフラグは変化しませんが、XORではZフラグが変化してしまいます。これもメモリの値をゼロにしたい場合には、CLRをつかった方が有利になります。

OR Rx, Rx でキャリーフラグはリセットされない

Z80では、Cフラグをリセットするために、ORANDが使われていましたが、Z8000では、フラグがリセットされません。リセットには、RESFLG C を使います。


INC, DEC はキャリーフラグが変化しない

Z80もそうだったような気がしますが、実行結果によってZフラグやSフラグは変化するのに、Cフラグには影響しません。INCDECは、1から16まで増減量を指定できるので便利なのですが、Z80には無かったので、コードを書くときに指定を忘れがちです。

また、何か気づいたら書いていきます。

2019年6月22日土曜日

Z8001ボードの回路図

今回作ったZ8001ボードの回路図と写真をさらしておきます。回路図は、はんだ付けしながら間違いに気づいて変更した箇所もあるので、このままでは動かないかもしれません。おそらくCPUを手に入れて作ってみようとする酔狂な人はいないと思うので、大丈夫だとは思いますが。

回路図
  Z8001MB CPU
  Z8001MB Memory
  Z8001MB I/O

仕様
 CPU   Z8001A
 クロック 6MHz
 メモリー   SRAM 256kB  
      ROMなし Atmega164Pによるブートコード書き込み 


写真にある空のICソケットは、Z8010 MMUを実装しようとして失敗した残骸です。外すのが面倒だったのでそのままにしてあります。そのうちに再挑戦するつもりです。
回路図にあるZ8536 CIO はまだ実装していません。タイマーとパラレルポートはあると便利なので、そのうちつなぐ予定です。

Z8001のアセンブリ言語

GNU asで、Z8001のアセンブリコードを書く際の注意点を書き留めておきます。


セグメントアドレス


 アドレスを直打ちする場合は、アドレスのbit31を1にする必要があります。そうしないと、アドレスの上位2byteが短縮アドレスとして扱われてしまいます。アドレスをラベルで指定する場合は、この問題はありません。


例えば

    ld     r0, 0x01000008

の場合、アセンブルされたコードは、

    ld        r0, 0x0100
 addb  rl0, #xx  

のように、アドレスの上位2byte が短縮アドレスになり、下位2byteは別の命令になってしまいます。
バクなのか、そういう仕様なのかはわかりませんが、これで2日間ほどはまりました。
ちなみに、asでは、セグメントアドレスを <<セグメント番号>> で表記できません。

ダイレクトアドレッシング


 セグメントモードだと、アドレスが32bitになってしまい、アドレス指定に必ず 4byte 使われてしまいます。なので、変数はメモリ上に置かず、できるだけレジスタに置いた方が、アドレス指定に消費するメモリが少なくなります。レジスタはZ80に比べたら山のようにあるので問題はないでしょう。
 変数をメモリ上に取るときは、間接アドレッシングを使った方がメモリ効率は良さそうです。ローカルな変数は、Cのようにスタック上に置いた方が良いのかもしれませんが、アセンブリ言語で管理するのは面倒な気もします。

間接アドレッシング


 Z8001の場合、R0〜R7は、上位下位に分けて8bitレジスタとして使えるので、間接アドレッシングを使うなら、RR8以降を使ったほうがよさそうです。RR14はスタックポインタとして使うので、RR8, RR10, RR12を使うことができます。Z80のIX,IY プラス 1レジスタと考えると、ちょっと得した気分になれます。

2019年6月6日木曜日

Z8010MMU メモ

Twitterに書いたのですが、Z8010 MMUをせっかく手に入れたので繋いでみたところ、思っていたようには動作しませんでした。データシートを読んだり動かしてみてわかった事などを、忘れないよう自分用に書き留めておきます。

やったことは、Z8001の起動用コードをSRAMに書き込むため、Atmega164PからZ8010を経由してアドレスを出そうとしました。

・Z8010は高機能なアドレスラッチではない
 アドレスとセグメント番号を出してASをかけると、ラッチしてアドレスを出してくれると思っていたのですが、それだけではアドレスを出してくれません。Z8010は、Z8001からでるST0 - ST3の信号を見ていて、メモリアクセスの場合だけアドレスを出すようです。
 当たり前といえば当たり前なのでが、てっきり特殊I/Oデバイスとしてのアクセスのためだけに使われている、と思っていのが間違いでした。

・クロックに同期した信号が必要
 これも上と同じく思い違いから来た失敗です。Z8010は、Z8001と同じようにクロックに合わせてアドレスを乗せたり、ASやDSを操作しないと正しく動作しないようです。
必要もないのにクロックを繋がせたりはしないですね。


・単純にRESETをLowにしただけではダメ
 Z8010をリセットするのにRESETを単純にLowにしただけでは、初期化されますがDisableの状態になり働きません。これは、MMUを複数つなぐための仕様のようです。EnableにするにはCSを同時にLowにする必要があります。

・RESETをかけるとアドレス透過モードになる
 RESETとCSをLowにすると、「Z8010はリセットされ透過モードになり、アドレスを素通しする」と、データシートに書かれているのですが、これが未だ謎な点です。ラッチした状態でアドレスを出してくれるのか、本当にバイパスするだけなのかが読み取れません。
Atmega164Pで必要な信号を作ってZ8010経由でメモリに書き込もうとすると、AD0 - AD15にアドレスに続いて書き込みデータを乗せDSをLowにすると、アドレスが書き込みデータに置き換わってしまいます。

これらの情報は自分以外には全く役に立たないでしょうが、また何が判明したら追加していきます。

2019年5月11日土曜日

メモリ周りから

Z8010 MMUは手に入れたものの、とりあえず今回は使わないでおきます。

Z8001/2のZ-BUSは結構ややこしくて、頭がいま混乱しています。Z8000 Technical Manual を参照しながら、頭の中を整理しがてらこれを書いています。

ICのピン数を減らすためなのか、アドレスバスとデータバスが共用になっており、AD0-15にアドレスを先に出してから、AD0-15を通してデータの入出力を行います。メモリーアクセス中はアドレスを保持しておく必要があるので、ASの立ち上がりエッジでアドレスが確定後、ラッチを使い保持します。
Z8001の場合は、SN0-6もメモリアクセス中に一時的にしか出ないので保持する必要があります。

Figure 9-3 より

次は、メモリとアドレス・データバスの接続。これがややこしい。
まず、Z8001にはダイナミックバスサイジングの機能がないので、16bit分のデータバスの配線をしなければなりません。AD0-7 は奇数アドレスのメモリに、AD8-15は偶数アドレスのメモリに接続します。なんとなく逆な気がするのですが、下の図のとおりです。
Figure 9-4 より
偶数・奇数アドレスメモリの選択ですが、図にあるように(メモリ選択は正論理)、

 バイトリード : B/W = H, R/W = H で、遇奇両アドレス選択 
    バイトライト : B/W = H, R/W = L で、AD0 = 0 が偶数アドレス選択、 AD0 = 1 が奇数アドレス選択  
    ワードリードライト : B/W = L, R/W = L or H で、遇奇両アドレス選択

になります。
バイトリードが両方選択なのは、CPUがAD0-7かAD8-15を選択して取り込むからです。このあたりは 9.4.2 Memory Transactions に書いてあります。

図ではメモリアドレスにはAD0から繋ぐような印象を受けますが、実際にはAD1からつなぎます。

ここまで書いてなんとなく頭の中が整理されてきました。というか思い出してきました。
年齢のせいか理解するのに時間がかかるし、字は見づらいわで苦労します。

2019年5月6日月曜日

BUSREQの確認

昨日はCPUの起動を確認しましたが、今日はBUSREQを確認してみます。
これが効くと、CPUはバスを明け渡してくれるので、RAMにブートコードを外部から書き込めます。

ATmega328Pにコードを書き足して、BUSREQをLowにしてHighに戻す操作を繰り返してみました。CPUはBUSREQを受けると、次のサイクルでバスを手放してBUSACKをLowにしてくれるはずです。この様子をオシロで見て確認しました。


CH1(黄色)がAS、CH2(青色)がBUSACKの波形です。BUSACKがLowになりASも出なくなっています。BUSACKがHighに戻るとASが再び出ているので、うまくいているようです。
まあ当たり前の事なのですが、こんな単純な事でも意図した通りに動いてくれると嬉しいですね。
これで先に進める目処が立ちました。

今回試した回路図をつけておきます。KiCadとか使い慣れていないので変なところがあるかもしれません。なんせ、今まで方眼紙に鉛筆で書いていた人間なもんで。
GWは今日でおしまいですが、この先もぼちぼち続けます。

2019年5月5日日曜日

Z8001に火を入れる

ATmega328Pを使って、Z8001のバスをリセット後に奪えるのか確認してみます。
懲りずにブレッドボード上に回路を組んでみました。

CLOCK, *RESET, *BUSREQは328Pにつなぎ、割り込み、トラップなどはプルアップ、AD0-15はプルダウンして、常に0x0000を読み込むようにしてあります。

CLOCKは328PのPWMを使って作っています。データシートによると、Z8001の最低動作クロック周波数は500kHzのようですが、動作を確認しやすいようにもっと下げて20kHzにしてあります。

まずは、クロックを供給しながら、*RESETを L->H だけしてみました。これで、CPUにリセットがかかり、ADDB RH0, #0 がひたすら実行しているはずです。行儀のいい暴走といったところでしょうか。
オシロで、*AS と *DS を見てみます。


CH1(黄色)が*AS、CH2(青色)が*DSです。短めの*ASに続いて*DSが長めにLowになる動作を繰り返しているので、正常に動作しているようです。

次は、*BUSREQを確認してみます。

2019年5月3日金曜日

ROMをどうするか

CPUは手に入ったわけですが、起動するにはROMが必要です。残念ながら手持ちがありません。

考えられる手は、素直にUV-EPROMを購入することです。幸いまだ出回っていて買えるようですが、ROMにプログラムを書き込むライタと消去するのに紫外線ランプも必要です。以前は、どちらも持っていたのが、やはり断舎利の対象となってしまっていました。

もう一つの方法は、ネットで見つけたAtmelのマイコンチップをつかってRAMに起動用のプログラムを書き込んでスタートさせる方法です。ブートコードはチップに書き込んでおきます。
昔のトグルスイッチでぱちぱちブートコードを書き込んでいたのを高級にした感じでしょうか。情報を手繰っていくと、Z80-MBC2: 4ICs homemade Z80 computer に行き着きました。クロックやリセットの信号もチップで作っているようです。

下手したらZ8001よりAtmelのチップのほうが高速だったりするかもしれず悩みどころですが、こちらの方法のほうが時間とコストがかからなそうです。はんだ付けの量も減らせそうなので、こちらを検討してみます。

問題は、リセット後にAtmelのチップがBUSREQでバスを握れて、CPUがBUSACKを返してくれるのかどうかです。
Z8001 Technical Manual の 9.6.2 Bus Request (p.262) によると、BUSREQはマシンサイクルの最初のクロックの立ち上がりで捉えられ、次のサイクルでBUSACKが返されます。

7.5 Reset (p.239) によると、Z8001はリセット後、連続したメモリリードサイクルに入り、0x0002からFCW、0x0004からセグメント番号、0006からPCの値を読み込み、この3サイクルの後、PCの値からの命令を実行するサイクルに入ります。この時点まできてやっとBUSREQを受け付けてくれるようになるようです。

このリセット後の3サイクルのリードサイクルに、0x0000を連続して読ませても大丈夫なら、ADをプルダウンしておくだけですみます。

2.7.1 Program Status Registers (p.26)によると、FCWが0x0000だと、ノンセグメント、ノーマルモード、割り込み禁止、EPAなしの設定になるだけで特に問題はなさそうです。

最初に実行される命令は、セグメント番号:00、 PC:0x0000にある0x0000と続く0x0000なので、
  ADDB RH0, #0
が実行されます。CPUは、ずっとこの命令を実行し続けることになり、BUSREQがかかるとバスを明け渡してくれるはずです。たぶん。

石が来た

SRAMの動作チェックを数回繰り返して、最終的に4個、512kbyte分に絞りました。
外した4個は動作が不安定なので、使うことは諦めます。謎のエラーに悩まされるのは嫌ですからね。

ただ、毎回違うアドレスで違うビットでエラーがでるので、ひょっとしたら、ピンやワイヤーの接触不良なのかもしれません。ブレッドボードは、ワイヤーを差し込んで配線しているだけなので、そんなに信頼性高くないでしょう。そのうち、ちゃんとはんだ付けしたテスターを作ってみるつもりです。
外した4個も念のため捨てずに取って置きます。

そして今日、eBayで注文していた石がやってきました。


届くのはGW明けだろうと思っていたので、郵便受けに入っているのを見つけてテンション上がってます。郵便局員さん連休中の配達ありがとうございます。

届いた石は、
 Z8001A CPU   6MHz
 Z8010B MMU  10MHz
 Z8530B SCI        8MHz
 Z8536B CIO       8MHz 

MMUは、使うかどうかはわからなかったのですが、あとで手に入らなくなって悔やむぐらいならと一緒に買ってしまいました。前回、学生のときに組み立てた時は、MMUは手に入らなかったので使っていません。無くても特に問題はありませんが。
CPUが6MHz動作品なのにMMUが10MHz動作品と、チグハグになってしまいましたが、そのうちCPUも10MHz品が手に入ったら差し替えたいと思います。

石はZilog製ではなく、SGS Thomson製で、Z8536以外は刻印から韓国で製造されたものです。以前持っていたCPUはZilog製の10MHz品だったので、捨ててしまったのが本当に悔やまれます。

意外だったのは、Z8536がPLCCだったことです。てっきりDIPだと思っていたので、箱を開けたとき目が点になりました。これ用のソケットが必要ですね。それと、これはマルタで製造されたようです。



------------------
5/4 追記

SRAMのテストプログラムを見直し何箇所か修正したところ、8個とも生きていることがわかりました。未使用のピンの処理がまずかったようです。ピンを出力に変更したり、入力でプルアップに設定したりで修正しました。
以前も同じ失敗をしたような気がしますが、その時の苦労も時間が立つと忘れてしまうものですね。

2019年5月2日木曜日

手持ち部品の確認 SRAMは生きているのか 続き

SRAMの動作は、各アドレスに1,2,4,8...と各ビット立てたをデータを書き込み、同じアドレスから読み出して比較をするコードを使って、全アドレスで正常にデータを読み書きできるかで確認します。
各アドレスのテスト結果はシリアルインターフェース経由で受け取ります。


このチェック方法では、アドレスラインが壊れていても、同じアドレスを結果的に読み書きしていた場合には正常と判断されてしまう恐れはありますが、あまり考えすぎないことにします。


1個めは0x1ffff まで正常に読み書きできています。

結果がでるのにSRAM1個あたり15分程度かかりますが、youtubeでも見ながら裏で1個づつ確認していきます。結果は8個中6個生き残っていました。なかなかいい成績でしょうか。ちょっと怪しい気もしますが、これで先に進めます。他に見つけてあった高速SRAMは、配線し直しで面倒なので、確認はまた気が向いたらにします。

次は問題は、ROMをどうするかですね。

2019年5月1日水曜日

手持ち部品の確認 SRAMは生きているのか?

令和一日目です。

ジャンク品の1Mbit SRAMは生きているんでしょうか?
ソケットは使うつもりですが、はんだ付けした後で動かないと分かったら、かなりのショックです。 SRAMが何個ぐらい生き残っているのか確認します。

ブレッドボード上に、手持ちのATmega328Pを使った簡易テスターを組み立てました。

1Mbit SRAM だと、データバスに8bit分, アドレスバスに17bit分、その他の制御線も必要なので、328PだとI/Oのピン数が全く足りません。アドレスは8bitフリップフロップ 74LS273を2個使って保持するようにしました。データバス、アドレスバス、CS, OE,  WE など各線を繋いでいくと、配線が山盛りです。線が交差しまくっているので、チェックするSRAMが交換できるのか不安です。
ネットで検索すると、Z80マイコンボードをブレッドボード上に作っている例がありますが、配線のセンスがない私には無理そうです。

ATmega328Pに、フリップフロップにアドレスを保持するコードと、データバスを読み書きするコードを書いて動作することは確認しました。

2019年4月30日火曜日

Z8001 クロス開発環境

電脳伝説Z8000のクロス開発環境 を参考に、クロス開発環境を構築してみました。

解説では、Windows上のVirtual BoxでFedora の 32bit Linuxを動かして、その上でクロス開発環境を動かしているのですが、私が普段使っている環境はUbuntuの64bitです。

説明に沿ってダウンロードしたファイルを展開して実行しようとしたのですが、"No such file or directory" とエラーが出て実行できませんでした。objdump で確認すると、実行ファイルは、elf32-i386 で、そのままでは実行できませんでした。

64bit 環境で実行するには、apt で lib32z1 をインストールすることが必要です。バージョン確認だけですが、z8k-coff-as が起動することを確認しました。


バージョンは、2.19 ですね。

2019年4月27日土曜日

手持ち部品の確認 その2

今年のゴールデンウィークは特に予定もなく、時間が有り余っているので、有効に使いたいものです。

手持ちのTTLの山の中にラッチや、アドレスデコードに必要そうなICとかが残っているのか確認しておきます。Z8001では、アドレスバスとデータバスがマルチプレクスされており、アドレスを保持するのにTTLのゲート回路だけでなく、ラッチが必要になります。8bitラッチがアドレスバス用に2個、セグメントアドレス用に1個の計3個必要です。


昔あった「74シリーズIC規格表」とかを持っていないと、型番から何のICなのかをネットで調べるのは結構骨が折れます。どこかまとまったサイトがあると助かるのですが。

74LS00や74LS32のようなゲート回路から、LS244,245のようなバッファまでなんとかあります。しかし、8bitラッチはLS373が1個しかなく、買い足す必要があります。あと、アドレスデコーダに使える3bit入力のLS138はあましたが、2bit入力のLS139が無かったので、このあたりも買い足しが必要かも。TTLも購入できる品種が減ってきているので、手に入るかちょっと心配です。

ジャンクボックスの中に、Lattice GALの20V8B,  22V10Bも見つけたのですが、プログラマーを持っていません。これが使えれば、TTLが手に入らなくても回路を組めるし、配線も手抜きできるのですが。

2019年4月26日金曜日

手持ち部品の確認 その1

発注したZ8001と周辺の石が出荷されたようなので、組み立てるのに使えそうな手持ちの部品を確認しています。

まずは、


SONY の 1M bit S-RAM CXK581000AP-10LL です。ジャンク品から外したもので8個 1Mbyte 分あります。一度も使ったことがないので、動作するのかわかりません。問題なければ、十分なメモリーが確保できます。

次、

256kbitの高速S-RAM IS61C256A-20Nが9個。むかしのPCのマザーボード上にキャッシュメモリーとして載っていたものです。8個使えばメモリー空間を256kbyte埋められます。 これも動く保証はありませんが、メモリ容量はこれで十分かも。高速なのでメモリアクセスにも余裕がありすぎるくらいです。この他にもメーカーの異なる9個を持っています。

次、


だんだんしょぼくなりますが、64kbit S-RAM 6個。これで48kbyteが埋められます。流石にこれでは少なすぎるか。データバス16bit分ハンダ付けの苦労が報われません。

部品箱を漁ったところ、RAMは手持ち部品で確保できそうです。万が一動かなくても、S-RAMはまだ購入できるので問題なさそうです。

問題なのはROMです。昔はUV-EPROMも複数個持っていたはずなのですが、今は手元にありません。今更新品を購入しても、プログラマーやUV消去機まで調達せなばならず、出費が増えるので気乗りがしません。秋月で、フラッシュメモリを購入できるようなので、これを使うか思案中です。

それにしても、S-RAMばっかり残っているなんて。今更ながら、何を基準に断舎利したのか、自分でもよくわかりません。

2019年4月22日月曜日

Z8001の情報 

Z8001のボードを組み立てるのに必要な情報の続き

ftp://ftp.groessler.org/pub/chris/olivetti_m20/doc/english/
 Z8001のデータシートとプログラムガイドがあります。多少読みにくい部分がありますが、これがないとどうしようもないので貴重な情報です。

Digital Research Source Code
 CP/M-8000のソースコードとバイナリが置いてあります。将来的には、CP/Mを動かしたいところですが、CP/Mを移植するには、Olivetti M20というマシンが必要なようです。

Olibetti M20 Site
 Z8001を搭載したパーソナルコンピュータ Olibetti M20の情報が集めてあります。 どこの国にもレトロ好きな人たちはいるものですね。そのパワーには驚かされます。ハードウェアのマニュアル類も公開されているので、CP/Mを移植するのに必要な情報が得られると思います。

Mame
 アーケードゲームのシミュレータソフトの公式サイトです。シミューレトできるCPUの中にZ8001/Z8002 も含まれているようです。役に立つかは、わかりませんが。

2019年4月21日日曜日

Zilog Z8001

最近、Zilogの16bit CPU Z8001のことをふと思い出した。本当にふとで、何がきっかけなのかわからないのですが。

今を去ること約30年前、大学生だった私は、趣味で8bitのZ80のアセンブリ言語でプログラムを書いていたのですが、貧乏学生に16bit CPUのパソコンは買えるはずありませんでした。
当時は、intelのx86, MotorolaのMC68Kがメジャーだったのですが、ひねくれ者の私は、「やっぱりZilogやろ」ということで、アルバイトで稼いだ金でZ8001と周辺の石を買い、 はんだ付けしてワンボードのコンピュータを組み立てて遊んでいました。

就職で故郷を離れ結婚をし子供を育てるうちに、作ったボードは捨てましたが、いつかまた組み立てて遊ぼうと、石は外してパーツボックスに放り込んでおいた、はずでした。

子供も成長し手が離れてきたし時間に余裕もできたので、また組み立ててみようと思い、そのパーツボックスを探しましたが、見つからない.....。各種CPUと周辺の石を入れていたボックスごとないので、引っ越しを繰り返すうちに、どうやら断舎利されていたらしい。TTL 類を入れたボックスは手元に残っているし、捨てた記憶も無いので不思議なのですが、無いものはない。
無いとなると無性に欲しくなり、国内の通販サイトとか当たりましたが、流石にもう古すぎて見つかりません。ネットをさまよっていると、ebayでドイツから買えることがわかり、衝動的に注文してしまいました。

石が届くのが待遠しいのですが、それまで情報収集することにします。なんせ、データシート類も一切なくしているので、組めないし動かしようもないので。

ネット検索してみると、Z8000の情報はそこそこあるようです。

電脳伝説
 Z8001ではなくノンセグメントのZ8002 を動かしてらしゃいます。開発環境の整え方とか記述があり、ハンドアセンブルしなくてすむので 、この情報は助かります。って、「モトローラ6800伝説」の著者の方なんですか。妙に納得しました。

Electrelic 
 このブログもかなりdeep。 Z8001と周辺の石を所有しており、そのコレクションがすごい。Goole検索でたどり着いたブログなのですが、CP/M-8000の存在を思い出させてくれました。プロフィールを見ると年齢はほぼ一緒(同級)の方で、MC68kでCP/Mを走らせたりと、私とは全く次元が違います。懐かしい単語が並んでいて読んでいてニマニマできて楽しいです(かなり、おじさん化してるわ)。

情報収集は続く。