F-BASIC版、OS-9版に続いてFLEX9版を作成すると共にOS-9版を微修正しました
まずはWindows側のslwinft245.exeです。
FM側のドライブソフトです。
F-BASIC版のSLWINFT |
OS-9版のslwinft |
FLEX9版のSLWINFT |
F-BASIC版のSLWINFT |
OS-9版のslwinft |
FLEX9版のSLWINFT |
Windows側のslwinft245 |
OS-9側のslwinftos9のメニュー |
メニュー1の実行例 |
いつも興味深い記事が連載されているskyriverさんのブログ「PICマイコンは面白い」で、以前、PC-G850Vを入手されたという記事を読み、そういえば昔、同僚が面白いと言っていたポケコンだなと思い出し、私も入手してみました。同時に、skyriverさんが作られたUSB-シリアルケーブルを見習って同様なケーブルを作って使用してきました。
それがこれです。
以前作成したケーブル |
そのアダプタ部 |
動作画面 |
【2024年1月24日】末尾にOneDriveを追加して、RAMディスクのない従来のFT245カード用のドライバ一式を上げました。
ブログ「思いつきハードでソフトに七転八倒(https://vehwk3yxv7hw.blog.fc2.com/blog-date-202206.html)」のshujiakitaさんの発案・製作によるFM-7/77用の「FT245使用の高速通信カード」と「RAMディスクカード」について、プリント基板を作成したり、両カードの合体カードを製作したりしながら、その都度それらをブログで紹介してきました。
FT245通信カード用のディスクイメージ転送専用ソフトhttps://flexonsbd.blogspot.com/2023/01/ft245.html
FT245通信カードとRAMディスクカードの合体カードの製作https://flexonsbd.blogspot.com/2022/06/ft245ram.html
Ndittを使用しないでGOTEK用のHFEファイルを作成する(その3)https://flexonsbd.blogspot.com/2020/12/ndittgotekhfe_11.html
振り返ってみますと、2020年12月11日のブログから始まって、上記のように8回も紹介していますが、それだけ私にとっても非常に有用なツールであり手放せないものになっています。
今回新たに基板を作成することにしました。回路には見直すところはありませんでしたが、念のために電源ラインを強化すること、FT245カードにはコネクタの取り付け穴の追加と、合体基板で採用したTXE#をソフトで読めるようにする切り替えスイッチの追加を行いました。
製作した3種のカードです。
製作したカード3種 |
(1)上左のカード:オリジナルのカードで秋月電子製のFT245モジュールを使用しています。
(2)上右のカード:オリジナルのFT245モジュールをFT245RLチップに置き換えたものです。マイクロUSBケーブルも使用できるようにマイクロコネクタも装備しています。
(3)下のカード:(2)にRAMディスクカードを合体させたものです。(2)と同様にマイクロコネクタも装備しています。
使用するドライブソフトは従来のものと変わりませんが、メニューを多少変更しました。
(1)shujiakitaさん作成の高機能ソフト(転送機能のみでなく様々な機能があります)
FM-7側:メニューソフト:FM7FDRAM(従来のFM7FDUIXのメニューを多少変えましたのでFM7FDRAMと改名)、機械語サブプログラム:FDIMGFMX, XRAMDK77
Windows側:FDDIMG_RW77.exe
(2)私作成の単機能ソフト(使いやすさを求めて転送機能に特化)
FM-7側:メニューソフト:FT245DRV、機械語サブプログラムFTDRV11o
Windows側:ft245drv.exe
以上、FT245通信カードを多少手直しして製作したという紹介でした。
ドライブソフト一式をOneDriveに上げておきます。
【2024年1月24日追加】RAMディスクのない従来のFT245カード用のソフト(Windows側:FDDIMG_RW77.exe、FM-7側:FM7FDUI5, FDIMGFM2)をOneDriveに上げました。なお、FM7FDUI5, FDDIMGFM2はFB_TRANS.DATの中にも入れてあります。
FM77AV20/40には2台の2DDドライブが装備されていますが、時には2Dドライブ(5インチや3.5インチ)を増設したい時があります。
普通のドライブ増設の手段としては現在のドライブが接続されているケーブルにさらにドライブを接続する方法があります。FDC(フロッピィディスクコントローラ)として通常使用されているMB8877AやMB8866はドライブが4台まで接続できるので、この方法で増設できそうなのですが、実際に試みてみるとうまくいきません。2D、2DDドライブのどちらも正常に動作しないのです。
正規の方法としては、拡張ドライブ接続用の専用のアダプターFM77-732を使用することになっていますが、現在入手するのは困難です。
ということで手持ちのFM77AV40SXにドライブを増設することはあきらめていたのですが、最近、知人がSXを入手されたところ、それにFM77-732が取り付けられていました。
これが純正のFM77-732カードです。
オリジナルのFM77-732カード |
早速そのFM77-732をお借りして調べてみました。
使用されているICは74HC04(2個), 74HC05, 74LS14, MB463(3個)の計7個のコンパクトなカードでしたが、ネット検索してもMB463の規格が分かりません。
基板のパターンは単純な二層基板なので、目視と導通テスターで調べて回路図を起こしてみました。ここに回路図を載せるのは差し障りがあるかもしれませんので掲載しませんが、基本的には単なるバッファ回路でした。
ただし、HeadLoadとStepPulseという2信号のみはフラットケーブル(つまり内蔵のドライブへの信号)からではなく、別途メインボードの5ピンソケットから取っています。内蔵ドライブへの信号は2DD用ですので、それとは別にメインボード上で2D用の信号を生成しているのだと思われます。
回路図の検討からMB463は通常のバッファICの7438で代用できそうだという見込みが立ちましたので、プリント基板を起こすことにしました。相変わらずドジをしてしまって作り直すことになりましたが、2枚目で無事に動作しました。
使用する部品のほとんどは何とか入手できましたが、5ピンコネクタだけはメインボードのソケットに合致するものが見つかりませんでした。。。
作成したプリント基板です。
作成した基板 |
製作したFM77-732互換カード
製作した互換カード |
このカードをFM77AV20/40の背面の拡張FDDのコネクタ取り付け部に固定し、メインボードのFDDへのケーブルを抜き、そこにフラットケーブルの先端のコネクタを装着し、抜いたケーブルは画像の白の中継コネクタに嵌め込みます。
5芯のケーブルはメインボードのコネクタに装着したいのですが、画像のコネクタでは合いませんので、両端がオス・メスのブレッドボード用のケーブル5本を用いて繋ぎます。
なお、37ピンD-Subコネクタと増設ドライブとの接続ケーブルは信号のピン配列が一致していますので画像のようにフラットケーブルの両端にコネクタを付けたものでOKでした。
FDD接続ケーブル |
全体の接続図
FM77-732カードの接続図 |
接続テストとしては、2Dの3.5インチドライブYD-625を使用しましたが、DISK-BASICの起動時にドライブ数として3以上を設定することで、特に問題もなくドライブ2として読み書きができました。
間に合わせに使用したケーブル |
唯一の問題は5ピンのコネクタが入手できないということですが、画像のようなオス・メスケーブルでとりあえず間に合わせることができました。
回路図を公開できれば自作してみたいという方がおられる(?)かもとは思いますが、このような数十年前のメーカー製品の回路図の公開は許されるのでしょうか。
以前、2021年6月15日のブログ「アップスキャンコンバータの製作」で紹介しましたアップスキャンコンバータですが、FM-7に接続して2年ほど使用していました。機能面では特に問題はなかったのですが、ベース基板が手配線であることと切り替えボックスへの収め方にとりあえず感があるのが気にかかっていました。
そんな時に、入手しにくい12接点のロータリースイッチがヤフオクに出品されているのを見つけて、もう一台製作して今度はプリント基板を作成し、ちゃんとしたケースに収めようと考えました。
製作したプリント基板が下画像です。(ミスを重ねて3枚目でようやく完成しました。)
作成した基板 |
以前のものはこのようにとりあえずの手配線でした。
第一作の手配線基板 |
オリジナルの作者であるNibbles lab. HomePageのOh!石さんが「RGBI対応スキャンコンバータ」で使われたケースはタカチのMX型モバイルケースMX2-8-10というアルミケースのしっかりしたもので、私も真似をして同じMX2-8-10SGを使いました。
なお、元の製作記事は上記サイト中の「研究成果->完了プロジェクト->RGBI対応アップスキャンコンバータ」です。貴重な製作記事を公開していただきありがとうございます。また、FPGAカードはデザインウェーブマガジン2007年7月号の付録基板です。
ケースに収めた状態です。
上蓋と背面パネルを取り外した状態 |
分解した様子です。
組み立てた状態とケースから取り出した状態 |
前面パネルと背面パネル |
前回のブログで、MC09でコンパイルした実行ファイルをASSIST09などで走らせる方法を紹介しました。
その中で、wnoy さんのサイト「小さな言語」中でPL/0をMC09でコンパイルしてMPB6809のモニター上で走らせておられたので、それを私も走らせてみたと書きましたが、実際には、それはMPB6809のモニターを実装してサイト中のバイナリリストを入力したものを、そのまま走らせたということでした。
前回も掲載した画像ですが |
上記サイトにはMC09でコンパイルした結果のアセンブルリストが掲載されていたので、その中のI/O関係を自分の環境に合わせて書き換えてアセンブルすれば実行ファイルが生成されるはずです。
しかし、実はこのようにして生成された実行ファイルは、最初は正常に動作しませんでした。
私が通常使用している6809用のアセンブラはFLEX09上のASMBと自作のWindows上で動作するクロスアセンブラです。加えて、リファレンス用としてアークピットさんのX6809を使用しています。
残念ながらPL/0のアセンブルリストはラベルの長さが6文字を超えているためにASMBではアセンブルできないので、自作のクロスアセンブラでアセンブルしていましたが、結果がwnoyさんのものと異なってしまうのです。これは自作のソフトのバグかと疑って、X6809でアセンブルしてみても結果は自作のものと全く同じでした。
誤変換されてしまう命令を示します。MC09によるコンパイル結果のアセンブルソースでは、文字列の表示に下記のパターンが多用されています。(十数か所あります!)
LEAX 2,PC
BRA ZZZZ <- 表示ルーチンへ
FCB $xx1,$xx2,$xx3,...,$xxn
ZZZZ (表示ルーチン)
これは、LEAX 2,PCで表示すべき文字列の先頭のアドレスをXに入れておいて、BRA命令で表示ルーチンへ飛んでいるわけですが、問題は先頭行の LEAX 2,PC で、これが正しく変換されていないのが正常に動作しない原因でした。
wnoyさんのサイトでは、変換結果は 30 8C 02 であるのに対して、自作のアセンブラやアークピットさんのX6809ではいずれも 30 8D XXXX となりますし、LEAX <2,PC と書き換えてみても X6809では 30 8C XX となるのですが、XXは02にはなりませんでした。
参考までに、X6809でのアセンブル結果を示します。
このLEAX命令の意味としては、LEAX命令の位置のPC値に2を加えた値をXの値とするということですから、EFFEやF2という値は明らかにおかしくて0002や02にならなければなりません。
めったに使うことのない命令でしたので気づくのが遅れましたが、自作のクロスアセンブラを正しく変換できるように修正し、改めてPL/0のアセンブルリストをアセンブルし直した結果、正しく動作する実行ファイルが得られました。ということで、ようやくPL/0をASSIST09や自作モニター上で走らせることができました。(長かった、、、)
ちなみに、この変換結果はアークピットさんのX6809だけでなく、FLEX09上のASMBでも同様で上記と全く同じ結果となります。ということは、これは誤変換ということではなく、このLEAX命令の解釈としては上記の変換結果の方が正しいということを意味するのでしょうか。しかし、実際に実行してみると、30 8C 02 でないと正しく動作しないのですが,,,
(wnoyさんがどのアセンブラを使用されたのかは分かりませんが、アセンブルリストの行末記号が$0Aですので、おそらくLinux上のクロスアセンブラas09を使用されたのだと推測しています。)
FLEX09には高機能で使いやすいアセンブラASMBがあって便利に使っています。さらに有名な整数型CコンパイラMC09もあります。
ASMBで作成した実行ファイルはもちろんFLEX09のない環境に移行して動作させることができるので、しばしばそのような使い方をしているのですが、MC09については、生成された実行ファイルがFLEX09と密接に関連していて、他の環境で動作させることはできないと思い込んでいました。
しかし以前、「小さな言語」というwnoy さんのサイトがありまして、そこではMPB6809というボードマイコンとそれ用のモニタープログラムが公開されていました。
(現在このサイトは存在しませんが、その内容を保存された方が紹介されています。「MPB6809」で検索するとヒットします。)
このサイトでは他にもForthの移植やTinyBasicの解析などもされているのですが、何とPL/0をMC09でコンパイルして移植しておられるのです。
私もこのモニタープログラムを入力してROMに焼き、自作の6809マイコンでPL/0を走らせてみました。PL/0は実用を目指したコンパイラではないので、特に使い続けるつもりはありませんでしたが、MC09でコンパイルした実行ファイルをMPB6809というマイコンに移植したという点に興味を持ちました。
FLEX09に依存しているファイルI/Oを使わないプログラムならFLEX09がなくても動作するのは当然ではありますが、初めて見る実例でした。このサイトを知ったのは十年以上前なのですが、それ以来、時々ですがMC09でコンパイルした結果のソースリストをいじって自作のマイコンで走らないかと試していました。
PL/0のソースは大幅に書き換えないとそのままではMC09でコンパイルできないので、まだPL/0をコンパイルすることはできていないのですが、他のプログラムについては、最近ようやく何とか動くようになりました。
動作試験に用いたマイコンは下画像のもので、2MHz動作の63B09と68B02を切り替えて使用しています。モニターは6809では自作のものとASSIST09を、6802では自作のものとMIKBUG2をROMに入れてあり、FlexDrvWin.exeという仮想ドライブ構築ソフトを用いることで6809のFLEX09と6800のFLEX2が走っています。
使用したボードマイコン |
MC09でコンパイルした結果はC.OUTというアセンブルリストとして出力され、それをMC09に標準で添付されているC.TXTというアセンブルリスト中に読み込んで、全体をアセンブルすることで実行ファイルが生成されます。
MC09 oooo.C --> アセンブルリスト C.OUTが作られる。
ASMB C.TXT oooo.CMD --> C.TXT中にC.OUTが読み込まれ、実行ファイル oooo.CMDが生成される。
このC.TXT中にコンソールI/OやファイルI/Oなどが書き込まれているので、コンソールI/Oを自分の環境に合うように書き換え、ファイルI/Oは不要ということで削除すれば良いはずなのですが、エラーの連発でなかなかうまくいかず、試行錯誤の連続でしたが、wnoyさんのPL/0のアセンブルリストを参考にしながら何とか動作するものを作ることができました。
もちろん、FLEX09のファイルI/Oを必要とするプログラムはダメですが、ファイルI/OがないプログラムならMC09で作成して、マイコン等で走らせることができます。サンプルとしてメモリダンププログラム mdump.c を作成してみました。
作成したのは、私の自作マイコンにインストールしてある自作モニター用のC_SBC.TXTとモトローラ社のモニターASSIST09用のC_ASSIST.TXTの2種です。
これらを標準のC.TXTの代わりに用いることで、自作モニタ―上やASSIST09上で動作する実行ファイルが得られます。
MC09 -O1.C.OUT oooo.C --> アセンブルリスト C.OUTがドライブ1に作成される
ASMB C_SBC oooo.BIN +YLS または ASMB C_ASSIST oooo.BIN +YLS --> 実行ファイル oooo.BIN がドライブ1に作成される
この実行ファイルをFLEX09のディスクから取り出してマイコン側にインストールして、$100から実行します。
(C.TXTにORG $100と書いてあるので、MC09で作成した実行ファイルは$100から実行することになっていますが、実は、アドレス依存のコードではないのでどのアドレスに置いても実行できます。)
作成したC.SBC.TXTとC.ASSIST.TXTを下記に示します。基本的にI/O関係のアドレスが異なるだけですが、ASSIST09のI/OルーチンはSWI命令を使っているのに対して、私の自作モニターは普通にJSR命令を使っているので、コールの仕方もそれぞれに合わせています。
C_SBC.TXT
C_ASSIST.TXT
(以下省略)の部分は標準のC.TXTの192行目以降と同じです。
参考までに、サンプルとして作成した mdump.c を示しておきます。このような、ファイルI/OがないプログラムはFLEX09がなくても動作するわけです。
mdump.c
出来あがった mdump.bin のサイズは4.2KBほどでした。サイズは大きくなりますが、アセンブラで書くよりも作成時間が短くなるので、サイズがそれほど問題にならない場合にはMC09を使用するメリットはあると思います。
参考までに、私の場合の具体的な実行ファイル作成・実行手順を示します。
1.WindowsPC上のエディタでソースプログラムを作成する。
2.FlexDrvWin.exe中のイメージファイル(.DSKまたは.D77)にソースをドラッグ&ドロップする。
3.MC09でコンパイル、ASMBでアセンブルする。
4.出来あがった実行ファイル(.BIN)をイメージファイルからWindowsPCへ読み出す。
5.バイナリ形式の実行ファイルを自作の変換ソフトCvtMotHexBin.exeを用いてモトローラ形式(.MOT)に変換する。
6.モニターのLoadコマンドでモトローラ形式の実行ファイルを読み込む。
7.モニターのGoコマンドで実行する。
以上ですが、作成・使用したファイル(C_ASSIST.TXT, C_SBC.TXT, 最新版のFlexDrvWin.exe, CvtMotHexBin.exe, サンプルのmdump.c)をOneDriveに上げておきます。なお、FLEX09やMC09の作成手順は以前のブログで紹介しております。
2022年10月21日に「FM77AV40用512KB増設RAMカードの製作」を紹介しました。製作した基板のサイズは純正の256KB増設メモリカードよりはるかに小さなものでしたが、それでもまだ基板に余裕がありましたので、512KBメモリをもうひとつ載せられそうです。ということで、容量を倍増させたカードを製作してみることにしました。
これが製作した1024KB増設RAMカードです。(実際に増加する容量は760KBです。)
512KBメモリはSOP型のHM628512を自作変換基板にセットしたものを使用しています。
1024KB増設RAM |
回路図を下に示します。
最初は通常のTTL-ICで試作しましたが、未使用のゲートが多く、あまりにも無駄が多いのでどうしようかと思っていたところに、知人からこういう時にはGALを使ったら良いかもとのアドバイスを受けて、GALに変更することにしました。
最初に試作した回路 |
Latticeの16V8Bを使用しましたが、3個のTTL-ICを1個のGALで置き換えることができました。
GALに変更 |
GALのプログラムにはMicrochip社のWINCUPLを用いました。
GALを使用するのは初めてだったのですが、実際に使用してみて、この程度のロジックをGALのプログラムに置き換えるのは、初心者でもそう難しいことではないと感じました。
参考までに、今回の置き換えの部分を示しますが、あとはそれぞれの入出力信号を16V8Bのピンに割り当てればOKです。
/* EQUATIONS */
CS1 = !(A18 & A19) ;
CS2 = !(A18 $ A19) ;
WE = !(!RW & E) ;
OE = !(RW & E) ;
前回と同様、OS-9 Level2でフリーメモリを確認しました。
その結果が下記ですが、760KB増えています。
フリーメモリ確認 |
512KBのメモリを2個搭載しているので1024KBの容量なのですが、拡張メモリの範囲は$40000~$FFFFFの768KBですので、これだけ増加するはずですが、実際には$40000~$FDFFFの760KBの増加になっています。OS-9の場合は、最上位の$FE000~$FFFFFの$2000バイト(8Kバイト)は使用不可のようです。
この位のRAM容量があれば、一部をRAMディスクに充てることもできそうです。
以上、512KBの増設RAMの容量をさらに増やしてみたという報告でした。
現在48ピンのコネクタの入手は困難ですが、コネクタさえ入手できれば回路は簡単ですし512KBのSRAMも入手は容易ですので製作そのものは容易です。
Z80版8ビットコンパイラ stellarを入力してみました
5月20日の「コンパイラ作成の試み その1「ハイクラスC言語」のリストを入力」で参考文献として紹介しました「Z80CPU対応 新言語作成の技法 (大貫広幸著 MIA社)」中に掲載されています、CP/M-80で動作するコンパイラ言語 stellarのアセンブルリストを入力してみました。
120ページ以上ありましたが、何とか入力してCP/M-80のMacro-80でアセンブルして実行形式を作成することができました。
作成したファイルは
・stellar.com コンパイラ オブジェクトファイルを生成
・convobj.com 生成されたオブジェクトファイルをHEX形式に変換
の2つで、コンパイルの手順は 下に例を示しましたが、stellar.com でコンパイル、convobj.com でHEX形式に変換、save.comでディスクに保存した後に実行となります。
d>stellar mdump Stellar compiler Rev 1.01 ( CP/M-80,MSX-DOS Version ) Copyright (c) 1984 H.Ohnuki / MIA Program name : mdump Function name : getchr Function name : putchr Function name : putnl Function name : putstr Function name : puthex Function name : puthex1 Function name : bdos Program 04EF (0100-05EE) Data 0047 (4000-4046) ** End of compile, No error(s) d>convobj mdump Stellar utility, convert object ==> intel HEX Rev 1.00 Copyright (c) 1984 H.Ohnuki / MIA Program name : mdump = 0100 Function name : getchr = 0482 Function name : putchr = 04A1 Function name : putnl = 04C4 Function name : putstr = 0511 Function name : puthex = 0549 Function name : puthex1 = 057A Function name : bdos = 05B6 Constant name : _work = 4000 Constant name : _var = 4030 Constant name : _code = 0100 Constant name : ngetchr = 0001 Constant name : nputchr = 0002 Variable name : d = 4030 Variable name : i = 4031 Variable name : sttadr = 4032 Variable name : endadr = 4036 Variable name : sadr = 403A Variable name : eadr = 403C Variable name : ch = 403E Data name : msttadr = 020D Data name : mendadr = 0217 Data name : header = 0221 End address : 05EE Program size : 04EF [ 5 Page ] d>save 5 mdump.com d> d>mdump sttadr= $1234 endadr= $1567 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF 1230: 55 E5 22 DD 55 CD E8 12 3E 32 2B CD 79 23 21 00 U.".U...>2+.y#!. 1240: 00 22 DF 55 3A 93 55 FE 7B 28 1F FE 9A C2 19 2A .".U:.U.{(.....* 1250: CD 1A 25 CD C5 17 FE 7B C2 19 2A 2A 6D 55 22 DF ..%....{..**mU". 1260: 55 CD E8 12 3E 32 2B CD 79 23 CD 50 15 2A DD 55 U...>2+.y#.P.*.U 1270: CD 48 22 2A DB 55 CD 48 22 21 F9 12 CD A5 22 2A .H"*.U.H"!...."* 1280: 69 55 2B 2B 22 D9 55 CD 93 11 2A DF 55 7C B5 20 iU++".U...*.U|. 1290: 19 2A DB 55 3E 21 CD 79 23 3E 34 CD 5E 23 2A D7 .*.U>!.y#>4.^#*. 12A0: 55 3E 20 CD 35 15 3E C2 18 19 CD 48 22 2A DB 55 U> .5.>....H"*.U 12B0: CD 48 22 21 04 13 CD A5 22 2A D7 55 3E 30 CD 35 .H"!...."*.U>0.5 12C0: 15 3E D2 DC 79 23 CD 68 15 E1 22 6D 55 01 06 00 .>..y#.h.."mU... 12D0: 21 DB 55 CD 0E 25 01 0F 00 21 73 55 CD 0E 25 21 !.U..%...!sU..%! 12E0: 73 55 CD 39 08 C3 1A 25 23 22 6D 55 EB 2A 6B 55 sU.9...%#"mU.*kU 12F0: B7 ED 52 EB D0 22 6B 55 C9 0A 3A B9 B9 21 BB BB ..R.."kU..:..!.. +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF 1300: 96 DA 00 00 08 3A B9 B9 21 BB BB 86 77 FE 23 CA .....:..!...w.#. 1310: 97 13 3D C2 19 2A 21 73 55 CD C0 08 B7 C4 7F 28 ..=..*!sU......( 1320: 21 73 55 7E E6 2F FE 02 C2 19 2A 01 0F 00 CD FE !sU~./....*..... 1330: 24 21 73 55 CB EE CD 39 08 2A DB 55 E5 2A 80 55 $!sU...9.*.U.*.U 1340: 22 DB 55 CD 1A 25 FE 2C C2 19 2A CD 1A 25 CD C5 ".U..%.,..*..%.. 1350: 17 FE 7B C2 19 2A 3E 32 2A DB 55 CD 79 23 CD 50 ..{..*>2*.U.y#.P 1360: 15 CD 93 11 3E 21 2A DB 55 CD 79 23 3E 35 CD 5E ....>!*.U.y#>5.^ 1370: 23 2A D7 55 3E 20 CD 35 15 3E C2 DC 79 23 CD 68 #*.U> .5.>..y#.h 1380: 15 E1 22 DB 55 01 0F 00 21 73 55 CD 0E 25 21 73 ..".U...!sU..%!s 1390: 55 CD 39 08 C3 1A 25 21 96 55 7E B7 C2 19 2A 2F U.9...%!.U~...*/ 13A0: 77 CD 1A 25 FE 2C C2 19 2A CD 1A 25 CD 00 18 47 w..%.,..*..%...G 13B0: 3A 98 55 3D C2 48 2A 78 FE 7B C2 19 2A FD 7E FD :.U=.H*x.{..*.~. 13C0: B7 28 11 21 F7 17 3D 28 08 21 FF 13 CD 9A 22 18 .(.!..=(.!....". 13D0: 08 CD 9A 22 3E 47 CD 5E 23 CD 50 15 CD 93 11 2A ...">G.^#.P....* 13E0: D7 55 3E 10 CD 35 15 30 0C E5 3E 05 CD 5E 23 E1 .U>..5.0..>..^#. 13F0: 3E C2 CD 79 23 AF 32 96 55 CD 68 15 C3 1A 25 02 >..y#.2.U.h...%. +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF 1400: 06 B8 2A E3 55 E5 2A E1 55 E5 21 00 00 22 E3 55 ..*.U.*.U.!..".U 1410: 22 E1 55 CD C5 17 FE 94 C2 19 2A 3E B7 CD 5E 23 ".U.......*>..^# 1420: 3E CA 21 00 00 CD 79 23 2A 69 55 2B 2B 22 E1 55 >.!...y#*iU++".U 1430: CD 1A 25 CD 91 10 3A 93 55 FE BD 28 2A FE 95 28 ..%...:.U..(*..( 1440: 08 2A E1 55 CD C2 23 18 0D CD 1A 25 FE 93 28 17 .*.U..#....%..(. 1450: CD 6F 14 CD 91 10 2A E3 55 7C B5 C4 C2 23 E1 22 .o....*.U|...#." 1460: E1 55 E1 22 E3 55 C9 CD 6F 14 CD 1A 25 18 A4 2A .U.".U..o...%..* 1470: E3 55 3E C3 CD 79 23 2A E1 55 CD C2 23 2A 69 55 .U>..y#*.U..#*iU 1480: 2B 2B 22 E3 55 C9 FE 3B C2 19 2A 3E C3 2A D9 55 ++".U..;..*>.*.U 1490: CD 79 23 2A 69 55 2B 2B 22 D9 55 C3 1A 25 FE 99 .y#*iU++".U..%.. 14A0: C2 19 2A CD 1A 25 3D C2 19 2A 21 73 55 CD C0 08 ..*..%=..*!sU... 14B0: B7 28 0D 3E 98 32 73 55 21 00 00 22 80 55 18 1E .(.>.2sU!..".U.. 14C0: 3A 73 55 47 E6 0F FE 08 C2 19 2A 2A 80 55 78 87 :sUG......**.Ux. 14D0: 38 0C 3E 18 CD 35 15 3E C3 DC 79 23 18 13 3E C3 8.>..5.>..y#..>. 14E0: CD 79 23 2A 69 55 2B 2B 22 80 55 21 73 55 CD 39 .y#*iU++".U!sU.9 14F0: 08 CD 1A 25 FE 3B C2 19 2A C3 1A 25 FE 3B C2 19 ...%.;..*..%.;.. +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF 1500: 2A 3A E9 55 B7 28 12 3E C3 2A EA 55 CD 79 23 2A *:.U.(.>.*.U.y#* 1510: 69 55 2B 2B 22 EA 55 18 05 3E C9 CD 5E 23 C3 1A iU++".U..>..^#.. 1520: 25 FE 3B C2 19 2A 3E C3 2A 63 55 11 03 00 19 CD %.;..*>.*cU..... 1530: 79 23 C3 1A 25 E5 F5 ED 5B 69 55 13 13 B7 ED 52 y#..%...[iU....R 1540: D1 5D 7D 87 9F BC E1 20 05 CD 6F 23 B7 C9 37 C9 .]}.... ..o#..7. 1550: DD E1 2A D7 55 E5 2A D9 55 E5 21 00 00 22 D9 55 ..*.U.*.U.!..".U 1560: 2A 69 55 22 D7 55 DD E9 DD E1 2A D9 55 7C B5 C4 *iU".U....*.U|..
stellar言語の特徴ですが、基本データ長は8ビットです。(68系のTL/1と同じですね)
しかし、64KBのメモリ範囲をアクセスできる関数が用意されているので、それ程不都合はないようです。また、制御構文も if, while, until, for, loop文があるのでプログラムが組みやすいようです。
特筆すべきは、inline文が用意されていて、プログラム中に機械語命令やデータを入れることができることと、インデックスレジスタ IX,IYを操作する文があることです。
書籍にはサンプルとしてファイルダンプ fdumpとハノイの塔 hanoi がありましたので、それらを参考にして、メモリダンププログラム mdump を作成してみました。
/* memory dump */ prog mdump(); cons ngetchr := 1, /* get chr from console */ nputchr := 2; /* put chr to console */ var d, i, sttadr[4], endadr[4], sadr[2], eadr[2], ch; data msttadr: "sttadr= $",0, mendadr: "endadr= $",0, header: " +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF",0; { /* input disp adrs area */ putstr(;.msttadr); for i:=0 to 3 { sttadr[i] := getchr(); } putnl(); putstr(;.mendadr); for i:=0 to 3 { endadr[i] := getchr(); } putnl(2); sadr[0] := (sttadr[0]-$30)*$10+(sttadr[1]-$30); sadr[1] := (sttadr[2]-$30)*$10+(sttadr[3]-$30); eadr[0] := (endadr[0]-$30)*$10+(endadr[1]-$30); eadr[1] := (endadr[2]-$30)*$10+(endadr[3]-$30); /* disp memory data */ sadr[1] := sadr[1] & $f0; /* start from adrlow=$x0 */ { putstr(;.header); putnl(); { puthex(sadr[0]); puthex(sadr[1]);putchr(':');putchr(' '); /* disp adrs */ for i:=0 to 15 { puthex(memory[sadr[0], (sadr[1])+i]); putchr(' '); /* disp by hex */ } putchr(' '); for i:=0 to 15 { d:=memory[sadr[0], (sadr[1])+i]; ch:= ?(d>=$20 & d<=$7e;d,'.'); putchr(ch); /* disp by ascii */ } putnl(); sadr[1] := sadr[1] + $10; } until (((sadr[0] <> eadr[0]) & (sadr[1] = $00)) | ((sadr[0] = eadr[0]) & (sadr[1] > eadr[1]))); putnl(); sadr[0] := sadr[0] + 1; } until ((sadr[0] > eadr[0])); } /***** subroutine *****/ getchr(); { bdos(ngetchr); } putchr(x); { bdos(nputchr, x); } putnl(n); cons cr := $0d, lf := $0a; var pn at ( _work ); { if pn=0 then n:=1; loop #,n { bdos(nputchr, cr); bdos(nputchr, lf); } } putstr(;ix); var x; { while x:=@[ix+] { bdos(nputchr, x); } } puthex(h); { puthex1(h>>4); puthex1(h); } puthex1(h); { bdos(nputchr, ?((h:=h & $0f)<10 0005h="" _work="" a="" argn="1" at="" bdos="" br="" c="" call="" cd="" d1="" dd="" de="" e1="" e5="" e="" else="" f="" fd="" func="" h="" hl="" if="" inline="" ix="" iy="" ld="" pop="" push="" then="" var="" x="">10>
この例で分かりますように、CP/MのBDOSを直接呼べますので、I/O関連のプログラムも容易に作成できます。ということで、GAMEやTL/1言語のようなマシンに密着したツールソフトを作成するのに向いている言語ではないかなという印象です。以上、書籍に掲載されていたCP/M版のZ80用の8ビットコンパイラを入力して、簡単なサンプルプログラムを作ってみたという報告でした。
もし、使ってみたいという方がおられましたら、メールで連絡をくだされば実行ファイル(stellar.comとconvobj.com)を返信メールに添付してお送りできます。 (もっとも、書籍中の構文や文法の解説を読まないと、使ってみるのもなかなか大変ですが。)
(表示用のアイコンとして入れてあります) |
前回からずいぶんと時間が過ぎましたが、言語の拡張がなかなかうまくいかず、四苦八苦していますが、とりあえず現在の状況です。
拡張したのは以下の3つです。
プログラム作成時にはコメントアウト機能は必須だと思いますので、/*と*/で囲んだ部分はコメントとして読み飛ばされるようにしました。
C言語に慣れているせいか PRINT文の書式には違和感がありましたので、プリントする式部分を括弧で囲むように書式を変更しました。
変更前:PRINT I, ": ", DATA[I];
変更後:PRINT (I, ": ", DATA[I]);
制御文としては、IF文、WHILE文、DO..WHILE文があるのですが、やはりFOR文があると便利ということで追加してみようと試みました。
1と2は何とか実現できたのですが、3のFOR文の追加については四苦八苦した挙句、一応それらしく動作するものができたのですが、動作が思ったようにはなりません。。。
例えば FOR (I=0; I<10; I=I+1) PRINT(I,","); の場合
結果は 1,2,3,4,5,6,7,8,9,10 となってしまうのです。
その理由ですが、FOR文を頭から見ていくため、まず I=0 の初期設定で I の値を設定し、次に I<10 の終了条件の真偽判定をして、真の場合は続いて I=I+1 の増分を実行してから PRINT文を実行した後にFOR文の先頭に戻るという手順を繰り返すために、繰り返しの1回目から制御変数の増分を実行してしまうためです。
この言語は全てをスタックに積む形式で行っているために、途中経過をどこかに保存しておくということができず、プログラムをシーケンシャルに実行していくために、このような結果になってしまっています。
(WHILE文やDO..WHILE文ではシーケンシャルに実行していって正しい結果が得られるような書式になっています。)
スタック言語ではFOR文のような、解析した順序とは異なる順序で実行しなければならない文は不可能なのでしょうか。それとも何か実現できるような手法があるのでしょうか。
(そういえば、中田先生のPL/0にもFOR文はありませんね、、、)
私の知識・経験不足のためにこれ以上は無理なようですので、言語の拡張はここまでにして、次は、中間言語に変換しているところを直接6809の命令語に変換するように変更して、6809機で動作するコードを生成するコンパイラに仕上げていこうと思います。
昔からコンパイラの自作に憧れていまして、入手した書籍は十数冊になると思いますが、本をいくら読んでも(もちろん、完全に理解できるまで読み込めていないのですが)、コンパイラ(あるいはインタプリタ)の構造についてはおぼろげには理解できたような気がしても、実際のプログラムをどのように構成すればよいのかが良く分かりませんでした。
目標は6809用の整数型のコンパイラの自作なので、特に具体的なコード生成手法を知りたいのですが、何しろコンパイラの本というのは理論的な本が多くて、、、
/***********************************************************************
**** ****
**** c_dec.c Micro C Compiler declaration procedure file ****
**** ****
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "c_mcc.h"
/***************************************************************
* vardec() 変数宣言の処理
* 戻り値: なし
* 引数: なし
***************************************************************/
void vardec(void)
{
do {
getsym();
if (sym != IDENT)
puterr("Variable ID expected.");
getsym();
if (sym != LBRCKT) { /* 単純変数 */
enter(VAR_SMPL);
gen(VAR_SMPL, lblcnt++);
}
else { /* 配列変数 */
getsym();
if (isdigit(sym) != 0)
puterr("Number expected.");
enter(VAR_ARY);
gen(VAR_ARY, lblcnt++);
getsym();
if (sym != RBRCKT)
puterr("']' expected.");
getsym();
}
} while(sym == COMMA);
if (sym != SEMICOL)
puterr("';' expected.");
getsym();
}
/***************************************************************
* function() 関数処理
* 戻り値: なし
* 引数: なし
***************************************************************/
void function(void)
{
int i, mflag;
int j;
source_f();
if ((i = position()) < 0) {
label(lblcnt++, fp);
// enter(FUNCID);
enter(UNDEFID);
}
else {
if (strcmp(id, "main") == 0) { /* if "main" */
j = 0;
while (strcmp(idtbl[j].name, id) != 0 && j <= idcnt) {
j++;
}
if (j < idcnt) { /* already exist */
if (idtbl[j].kind != UNDEFID) {
puterr("Symbol redeclarated.");
}
else {
mflag = ON;
label(0, fp);
// enter(BLTFUNC);
idtbl[j].kind = BLTFUNC;
idtbl[j].addr = j;
}
}
}
else if (idtbl[i].kind == UNDEFID) {
label(idtbl[i].addr, fp);
// enter(FUNCID);
idtbl[i].kind = FUNCID;
// idtbl[i].addr = lblcnt;
}
else
puterr("Symbol redeclarated.");
}
getsym();
if (sym == LBRACE) {
// i = 0;
// while (strcmp(id, idtbl[i].name) != 0 && i < idcnt)
// i++;
// label(idtbl[i].addr, fp);
getsym();
while (sym != RBRACE) {
statement();
if (sym == EOFSYM)
puterr("'}' expected.");
}
// if (strcmp(id, "main") == 0 && mflag == ON) { /* if "main" */
if (mflag == ON) { /* if "main" */
gen(JUMP, 1);
mflag = OFF;
} else
gen(RET, DUMY);
// }
}
else
puterr("'{' expected.");
getsym();
}
/***************************************************************
* statement() 文処理
* 戻り値: なし
* 引数: なし
***************************************************************/
void statement(void)
{
switch (sym) {
case IDENT:
assign();
break;
case IFSYM:
if_proc();
break;
case WHILESYM:
while_proc();
break;
case DOSYM:
do_while();
break;
case CALLSYM:
call_proc();
break;
case INPUTSYM:
input_proc();
break;
case PRINTSYM:
print_proc();
break;
case PRTLNSYM:
print_proc();
gen(NEW_LINE, 0);
break;
case LBRACE:
lbrace();
break;
case SEMICOL:
getsym();
break;
default:
puterr("statement expected.");
}
}