ブログの練習

ブログを書く練習です。
最近はレトロな計算機(電卓、マイコン、パソコンなど)
に関することを書き始めました。

Intel 4004 関連記事の目次

2099-12-31 23:59:59 | マイコン(4004)
goo blogでカテゴリ毎の記事一覧をうまく表示させる方法がよくわからなかったので、ここに4004関連の記事へのリンクをまとめておきます。

Intel 4004 (その1)
Intel 4004 (その2) クロックの巻
Intel 4004 (その3) 1702A EPROM用ライター(プログラマー)を自作する
Intel 4004 (その4) Lチカの巻
Intel 4004 (その5) Mask ROM 4001を読んでみる
Intel 4004 (その6) MCS-4 Evaluation Kit using the 4001-0009
Intel 4004 (その7) 実験用ボードの作成
Intel 4004 (その8) シリアル通信を実装する
Intel 4004 (その9) メモリ領域へのアクセス
Intel 4004 (その10) 電卓を作ってみる
Intel 4004 (その11) メモリ周りを改築する
Intel 4004 (その12) VTLインタプリタを作ってみる
Intel 4004 (その13) 8080エミュレータを作ってみる
Intel 4004 (その14) メモリ64KB化とプリント基板作成
Intel 4004 (その15) 周辺回路を現代の部品で作ってみる(Teensy編)
Intel 4004 (その16) 周辺回路を現代の部品で作ってみる(EMUZ80(PIC)編)
Intel 4004 (その17) 周辺回路を現代の部品で作ってみる(TangNano編)
Intel 4004 (その18) CPUを4040にしてみる
Intel 4004 (その19) VTLインタプリタを機能拡張する
コメント (3)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その19) VTLインタプリタを機能拡張する

2024-03-21 14:53:08 | マイコン(4004)

4004で動くVTLインタプリタでStarTrekを走らせる話です。

去年作ったVTLインタプリタですが、このときの目標はとりあえずマンデルブロ集合を表示するプログラムASCIIART.BASを走らせることだったので、未実装の機能がいくつかありました。

 

Intel 4004 (その12) VTLインタプリタを作ってみる - ブログの練習

4004用のVTLインタプリタを書いてみました。当初はMITS_Altair_680_Very_Tiny_Language_VTL-2_Manual.PDFに載っている6800版のオリジナルのソースか、白石孝次氏による8080...

goo blog

 

しかし、メモリ空間をほぼギリギリまで使っていたため機能追加は難しい状況。4004で動く8080のエミュレータがあれば8080用の言語処理系が動かせるのではないかと思って作ったのがこちら↓です。これでPalo Alto Tiny BASICやGAME80を動かすことはできたのですが、ちょっと実用にならないレベルの遅さでした。

 

Intel 4004 (その13) 8080エミュレータを作ってみる - ブログの練習

電卓やVTLインタプリタを書いてみて、ずいぶん4004のアセンブラに慣れてきました。とはいえ、いろいろなものを4004のアセンブラで一から作るのは面倒です。8080のエミュレー...

goo blog

 

というわけで、VTLインタプリタの方をもう少しちゃんとしたものにすることにしました。メモリ空間はモニタプログラムの機能を削って確保しました。

今回実装した機能は下記の通り。

  • 乱数(疑似乱数)
  • 配列
  • サブルーチンコールのネスト
  • 高速GOTO(通常のGOTOとは別の独自仕様)
  • 二項演算子(剰余, 排他的論理和)

古典的なコンピュータゲームであるStarTrekが走るレベルの機能を目標にしました。

まずは乱数。

疑似乱数の生成アルゴリズムというのは結構奥が深い世界なのですが、そんなに真面目に実装する気は無いし、そもそも4004には計算パワーも論理演算命令も無いので、とりあえず線形合同法でいいやと思って実装してみました。下記のような単純なものです。

uint16_t rand(){
static uint16_t x=1;
x=x*16877; /* 16877=7^5 */
return(x>>4);}

x_nをそのまま使うと出てこない数があったので>>4して0~4095までの乱数にしました。試しにx_nをプロットしてみるとこんな感じ。

周期性がありますが、まあわりと乱数っぽいです。ゲームぐらいには使えるかなと思ったのですが、後述するStarTrekで星がいつでも斜めに並ぶという現象が発生。

(x_{2n}, x_{2n+1})をプロットしてみると・・・

これはダメです。ちょっと使い物になりません。

というわけで、もうちょっと真面目に実装することにしました。疑似乱数生成アルゴリズムをググっていたところ、16ビットマイコンボードの製作 というページにxorshiftというアルゴリズムが紹介されているのを見つけました。

16 bit xorshift rng (now with more period)

聞いたこと無いなあと思って見てみたところ、どうやら2003年に提案されたアルゴリズムのようです。とてもシンプルなアルゴリズムです。

uint16_t rnd_xorshift_32() {
static uint16_t x=1,y=1;
uint16_t t=(x^(x>>5));
x=y;
return y=(y^(y>>1))^(t^(t>>3));
}

これが21世紀になるまで発見されていなかったというのは逆に驚きでした。

y_nをプロットしてみると、

この範囲では周期性っぽい模様は見えません。(y_{2n}, y_{2n+1})を見ても、

ちゃんと乱数っぽくなっています。良さそうなのでこれを実装することにしました。

しかしここで問題が。4004にはxorを計算する命令がありません。8080エミュレータのときに書いたコードをベースにして作ってみました。長いですが他に方法も無いのでこれを使うことにしました。

4004で16bitのxorを計算するプログラムです。

乱数生成部のルーチンはこんな感じになりました。

データRAMを16bitのレジスタとして使うためのサブルーチン群が既にあるのでこの程度に収まってます。ビットシフトは乗除算に使っているサブルーチンを流用しています。

これにより、システム変数 ' で0~32767までの乱数が得られるようになりました。0~99の乱数が欲しいときは、最初、

R=' / 100 R=%

のようにして求めていたのですが、見た目が悪いので剰余を求める二項演算子 % を実装して、

R='%100

と書けるようにしました。'(100)という記法にするより実装しやすいのでこうなりました。あと、せっかく排他的論理和のルーチンも作ったので二項演算子 ^ も実装しました。

乱数が作れたことにより、最古の経営シミュレーションと思われる「イスカンダルのトーフ屋ゲーム」を走らせることができました。動画はこちら。

8080エミュレータでは激遅でとても遊べない速度でしたが、今回はちゃんとした速度で動きました。

次に配列です。とりあえず1次元配列を1つだけ実装。記法は、@(x)にしました。TK-80BSのLevel 1 BASICをリスペクトしています。最初これをPEEK、POKEの機能にしようかとも思ったのですが、4004の機械語を書いてもそこにジャンプして実行できるわけではなく、データ格納以外に使い道が無いので、変数と同じ16bitの整数として読み書きします。

これでStarTrek走るかな?と思ってエンサイクロペディアASCIIのStarTrek特集を眺めてみると、多段のGOSUBが使われています。サブルーチンが1段だけのVTL用に移植されたもの(マイクロTREK)もあったのですが、配列に返り番地を積むとか、あまり美しくない移植だったので、多段のサブルーチンを真面目に実装することにしました。

GOSUBは現在実行中のコンテクストを退避してGOTOでジャンプするだけ、RETURNはコンテクストを復帰させるだけなので、それほど難しくはありません。退避するコンテクストは、

  • 実行中の行の先頭アドレス
  • 実行中のプログラム位置(GOSUB文!=xxxの直後)を示すアドレス
  • 実行中の次の行の先頭アドレス

としました。本質的なのは実行中のプログラム位置だけですが、今回の実装ではその前後もある方が作りやすかったのでコンテクストに含めています。

実行中の行の先頭アドレスは、後述の高速GOTO用のシステム変数 # のため、次の行の先頭アドレスはIF文不成立時に文末まで舐めずに次の行に飛ぶためのものです。

これでどうにかStarTrekを動かせるレベルの言語処理系が準備できました。

StarTrekのプログラムは結構長いので、どこかに転がってないかとググってみたところ、こちらのサイト(Bequest333のページ)で電大版StarTrekが入ったパッケージを入手。どうやら電大版Tiny BASICにはFOR文が無いようで、GOTOを#=に、PRを?=に、IFを;=にのように機械的に置き換えるだけでほぼそのままVTL-4004に移植できました。

さっそく実行してみましたが何かがおかしく、いろいろ調べてみたところどうやらエネルギーが0で何も出来無いという状態。安田寿明著「マイ・コンピュータをつかう」に載っているソースと比べてみたところ、なぜか330行目のGOSUB 1600が存在せず、エネルギーや光子魚雷の数が設定されなくなっていたのが原因でした。

そこを修正したところ無事ゲームで遊べるようになったのですが、速度がかなり遅いです。Short Range Sensorの表示に1分ぐらいかかっていました。

考えられる原因はGOTO文(#=)の処理です。今の実装では#=は飛び先の場所をプログラムの先頭から行番号を探してジャンプします。一応行番号の隣に次の行へのポインタを格納しているので、プログラムの文字列を全部舐めているわけではないのですが、それでもプログラムの後ろの方に

5000 I=I+1 ;=I<10 #=5000

みたいな行があると、#=5000でプログラムの先頭から順番に見ていって5000の行まで探すので大規模なプログラムだとかなりのオーバーヘッドになります。

ジャンプ先を行番号ではなくプログラムの場所のポインタで指定できれば即座にジャンプできます。というわけで、システム変数 # を、現在実行している行の行番号ではなく、現在実行中の行の先頭アドレスに変更することにしました。

さらに、実行するプログラムの位置を変更するための記法として、>= を実装しました。さきほどの例は、

5000 I=I+1 ;=I<10 @(I)=0 >=#

のように書き直します。16bitの値で普通に変数に格納できるので、ループを多重にしたい場合は A=#のように保存しておいて、>=Aでジャンプすることもできます。

ループで何度も実行されるGOTOをこれに書き直すことによってかなり高速化され、Short Range Sensorも30秒ほどで表示できるようになりました。

ゲームの様子です。まだ遅いと言えば遅いですが、ギリギリ耐えられる遅さにすることができたかなと思います。

インタプリタのソースコード等一式はGitHubに置きました。

 

GitHub - ryomuk/VTL4004: VTL Interpreter for 4004 Evaluation Board

VTL Interpreter for 4004 Evaluation Board. Contribute to ryomuk/VTL4004 development by creating an account on GitHub.

GitHub

 

 

 

コメント (1)
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その18) CPUを4040にしてみる

2024-03-04 14:27:10 | マイコン(4004)

1年前にeBayで買ったまま積んであった4040があったので、これを使った基板を作ってみることにしました。4040は4004と互換性が高く、ピンの配置を変換するアダプタでも作ればそのまま4004実験ボードに搭載してそのまま動かすことができるはずなのですが、せっかくなので拡張された機能も使えるようにします。

ハードウェアに関連する主な拡張機能は次の3つ。

  • 割り込み機能
  • プログラムメモリ領域が2倍(4KB→8KB)
  • シングルステップ実行

割り込みに関しては、通信ポートをポーリングではなく割り込みにするということも考えましたが、8251や6850のような通信用のLSIがあるわけでもなく面倒なので割り込みボタンを1つつけるだけにしました。

ROMのバンクを切り替えるための信号線CMROM1により、プログラムメモリ領域が2倍になりました。4004のときには8080エミュレータにしてもVTLにしても4KBのプログラム領域を使い切って足りなくなっていたのでかなりうれしいです。

シングルステップ実行は、アドレスやデータをラッチして表示したりしないと実用的じゃないし、使い道がいまいちイメージできなかったので信号線だけ出して未実装です。

ソフトウェアに関連する拡張機能には次のようなものがあります。

  • サブルーチン用スタックが3段→7段に増加
  • RPM命令によるプログラム領域のRAMの値の読み出し
  • 論理演算命令(AND, OR)

4004ではサブルーチンのネストが3段しか出来ず、プログラムを書く際の大きな制約になっていたのですが、少しはマシになりそうです。とはいえ、7段では構文解析みたいながっつり再帰的なプログラムには足りそうもないので使い道は限定されますが。

4004では256byteバンク内のメモリ値しか読めなかったので、4004実験機ではあらかじめ全部のバンクの末尾にメモリ値を読むためのサブルーチンを書いておいてそこをコールして読むという曲芸的なことをやっていました。4040ではRPM命令によって4289配下のプログラム領域のメモリの値を読めるようになります。

論理演算も4004ではえらい長いプログラムでなんとか実装していましたが、4040では少しマシになりそうです。

基板を設計するにあたり考慮した点を順不同で書き連ねます。

  • データRAMに4002-1を4つ使う構成はプログラムが書き難いので、4002-1と4002-2を各2つづつ使用。(切り替えジャンパは削除)
  • UARTのインターフェース部分はフォトカプラではなくTTLに変更。
  • 4201を使うとクロックとステップ実行の回路が簡単になるが、入手性が悪く、ステップ実行は実装しないので4004と同様にTTLを使用。
  • プログラム領域のRAMはHM62256は入手性が悪いのでHM628128に変更。
  • せっかくなので4004を搭載する用のソケットも用意しておく。

こんな方針で回路図を描いて基板を作ってみました。

さっそく組み立てて電源を入れてみたのですが、動かず。デバッグ開始です。

とりあえずクロックを見てみます。SYNC信号と2相クロックが綺麗に出ています。

次はメモリ回り。OEやCSはちゃんと出ていることが確認できたので、新規設計部分の拡張ROM領域関連が怪しい気がしてきました。

とりあえずA0とA12を見てみると、A12の幅が短い気がします。

A12は拡張ROM領域用のアドレス信号で、CMROM0=0のメモリアクセスのときに0、CMROM1=0のメモリアクセスときに1です。とりあえずCMROM0をそのまま出しておけばいいやと思って作っていたのですが、データシートを確認したところ、CMROM0はCPUサイクルのM1でしかアサートされないのでそこでラッチする必要があったのでした。

運良くNANDゲートが2つ余っていたので、CMROM=0で0、CMROM1=0で1になるようなRS-FFを作って修正。空中配線で配線しました。

電源を入れてみるとモニタプログラムが起動。4004に作ったVTLもそのまま動きました。

次は拡張機能部分の動作確認です。段階的に確認していきましたが、最終的に、

  • プログラム領域のRAMの読み込みにRPM命令を使用。
  • 割り込み時に拡張ROM領域に配置したプログラムにジャンプしてメッセージ表示。

というプログラムが動きました。動画はこちらです。VTLインタプリタでカウント表示ループを実行中にINTボタンで割り込みをかけて、拡張ROM領域に配置したプログラムで"*INT!*"というメッセージを表示しています。

回路図とガーバーデータはとりあえずGitHubに置いてあります。

 

GitHub - ryomuk/SBC4040: Single Board Computer using Intel 4040

Single Board Computer using Intel 4040. Contribute to ryomuk/SBC4040 development by creating an account on GitHub.

GitHub

 

最後に一言。

4040は確かに4004より強力で、プログラムも書き易そうではあるのですが、いろいろ作っていて感じたのは、4004のときほどのワクワク感は無いなあということでした。

というわけで、4040はこれでクローズして4004に戻ろうかと思います。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Z80をオーバークロックしてみる

2023-07-15 17:44:10 | マイコン

Z80(Z84C0020PEG)をオーバークロックして33MHzで動かすことに成功しましたので、とりあえず備忘録としてまとめておきます。

回路図やプログラム等一式はGitHubに置いてあります。

GitHub - ryomuk/tangnano-5V: 5V tolerant interface for Tang Nano

GitHub - ryomuk/tangnano-5V: 5V tolerant interface for Tang Nano

5V tolerant interface for Tang Nano. Contribute to ryomuk/tangnano-5V development by creating an account on GitHub.

GitHub

 

Tang Nano 20K用のGPIOを5Vトレラントにするアダプタを作ったのでその応用例としてZ80のメモリシステムを作ったというストーリーになっていますが、昔のCPU(Z80に限らず)のメモリシステムをTang Nanoに実装するために5Vインターフェースを作ったというのが正確なところです。

レベル変換用のICはSN74CB3T3245という長い名前のICを使用。レベル変換についていろいろググってたら見つかったこのページ(5V系・3.3V系信号レベル変換)で知りました。

データシートには「レベルシフタ搭載バススイッチ」とあります。5V→3.3Vはレベル変換しますが、基本的には「スイッチ」なので、3.3V→5Vは変換せずに3.3Vの信号がそのまま5V側に出力されます。5VTTLの閾値は1.5V,5VCMOSの閾値は2.5Vなので問題無く動作するということのようです。

レベル変換用ICにはSN74CB3T3245の他に、LBF0108、TXS0108、TXB0108というデバイス(いずれもTexas Instruments製)などもあり、TIのレベル変換関連の資料を見ると最近の資料にはSN74CB3T3245は載っておらず、LBF0108を勧めている感じもあったのですが、実物を入手していろいろ比較した結果、結局SN74CB3T3245が一番良さそうという結論に落ち着きました。

最初に作った変換基板にTangNano20Kを搭載したのがこれ。幅を間違えてしまっていました。

機能的には問題無いはずなのでとりあえずこれを使ってブレッドボードでZ80と接続。

なんとなく動いてるんだけど、メモリがちゃんと認識されてなかったりして不安定。

データシートを読み返していたらクロックはTTLレベルじゃだめでHレベルはVcc-0.6V必要ということを発見。

74AHCT04を使ってクロックを5Vまで引き上げたところわりと安定して動作するようになりました。

この雑な配線でも12MHzで動作。とはいえさすがにこの配線はダメだろうと思ってもう少し真面目に配線しなおしました。

そしたらブレッドボードで27MHzで動作してしまいました。ただちょっと不安定。ブレッドボードでは限界っぽいので基板の到着を待ちます。

基板が完成したので部品を載せ替え。

しかしおかしなことに27MHzでは動作してくれなくなってしまいました。20MHzでは動作するので回路に問題は無さそう。

メモリアクセスのタイミングの問題かなと思い、TangNano側のロジックを変更してみました。メモリはクロックに同期させる方が安定するだろうと思ってクロックエッジでMREQ_nやRD_n、WR_nを見て読み書きしていたのですが、(~MREQ_n & ~RD_n)や(~MREQ_n &~WR_n)のエッジで(クロックとは非同期で)読み書きするように変更したところ問題解決。変なことするよりメモリ本来の動作に忠実に実装する方が良かったようです。

安定して動くようになったのでいよいよオーバークロックの実験。Z84C0020PEGはデータシート上のスペックはDC~20MHzです。

まずは27MHz。電源はTangNano経由のUSB給電という適当な設定。これでもASCIIART.BASは無事完走しました。

データシートには、

とありますので、Vccの電圧は5.5V程度までなら安心して上げられます。

試してみたところ、クロック31.5MHzでVcc=5.55Vで途中で暴走。Vcc=5.6Vだとわりと安定して動作している感じでした。

31.5MHzというのはちょっとキリが悪いので、33MHzに挑戦してみることにしました。データシートを見ると、絶対最大定格は7.0Vです。

そういうことならVcc=6.0Vは大丈夫でしょう。試してみたところあっさり動作しました。

動画はこちら。

以上、Z84C0020PEGをVcc=6.0V, Clock=33MHzで動かしてASCIIART.BASを36秒で完走したお話でした。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

CH32V003でBASICインタプリタを動かしてみる

2023-06-30 15:13:52 | マイコン

秋月電子で売ってる40円のマイコン(CH32V003J4M6)がTwitterで話題になっていてちょっと気になっていたのですが、かんぱぱさん(@kanpapa)のブログにArduino IDEでの使い方がまとめられていて簡単に使えそうだったので試しに買ってみることにしました。

 

40円RISC-Vマイコン(CH32V003)をArduino IDEでLチカをしてみました | きょうのかんぱぱ

秋月電子さんに新商品のマイコンが入荷したようです。1個40円の32ビットRISC...

きょうのかんぱぱ

 

40円は税込価格で本体は37円。1個づつ個別包装です。袋とシリカゲルで数円分ぐらいかかってるのではないだろうか。書き込みにはWCH−LinkEエミュレーターなるものが必要とのことなので別途購入しましたが、これも750円と安価です。

スペックを見ると、

  • プログラムメモリ:16kB
  • RAM:2KB

とあります。このくらいあればBASICインタプリタが動くのではと思い、試してみることにしました。

とりあえず端末と通信できないことにはどうにもならないので、まずは通信周りについて調査。WCH-LinkEエミュレータにTX、RXなる端子があるのでこれを使えばUART通信ができるのかな?と思ってググってみたところ、こちらのブログとGitHubに関連情報を見つけました。

 

CH32V003をArduinoで使おう(CH32V203も) - Qiita

CH32V003 が Arduino で使えるようになりました。それでは、ArduinoIDEで使えるようにしましょう。ArduinoIDEに設定するファイル→環境設定追加のボードマネージャ ...

Qiita

 
 

arduino_core_ch32/variants/CH32V00x/CH32V003F4/PeripheralPins.c at main · openwch/arduino_core_ch32

Core library for CH32duino. Contribute to openwch/arduino_core_ch32 development by creating an account on GitHub.

GitHub

 

これによると、TXはPD_5=Pin 8、RXはPD_6=Pin 1のようです。

WCH-LinkEのTXをCH32のRX(Pin 1)、WCH-LinkEのRXをCH32のTX(Pin 8)に継げます。(ややこしいです。いつも混乱します。)

Pin 8はプログラム用の信号入力SWDIOも継がっているので共用になります。そのせいでトラブル発生。プログラムを書き替えようとしても継がらなくなってしまいました。

そういえば秋月のFAQに何か書いてあったような気がします。

32ビットRISC-Vマイコン CH32V003J4M6の質問と回答

なるほど。でもこれだけのためにMounRiver Studioを使うのは大袈裟だなあと思っていたところ、WCH-LinkUtilityにもこの機能があることを発見。こちらを使って無事クリアすることができました。

しかしすぐには動いてくれず、CH32→PCは継がるのに、PC→CH32の通信が出来ないという状態。原因を見つけるのに結構苦労したのですが、Serial.available()が常に-1なのが原因でした。RXにデータが来てるかどうかのチェックを、

if(Serial.available() > 0){
  c = Serial.read();
}

ではなく、

c = Serial.read();
if(c > 0){
  ...
}

とすることで通信できるようになりました。Arduino用の環境がCH32V003F4用に作られているせいなのかもしれませんがよくわかりません。

ともかくUARTで通信ができるようになったので、いよいよBASICインタプリタの実装に移ります。1から作るのは大変なので何か使えるものはないかと探してみたところ、電脳伝説さん(@vintagechips)の著書「タイニーBASICをCで書く」に掲載されている豊四季Tiny BASICがコンパクトで良さそうだったので試してみることにしました。ソースは本に記載されているサイトからダウンロードしたのですが、GitHubにほぼ同じものが公開されていました。

 

GitHub - vintagechips/ttbasic_arduino: TOYOSHIKI Tiny BASIC for Arduino. Tested in Arduino Uno.

TOYOSHIKI Tiny BASIC for Arduino. Tested in Arduino Uno. - GitHub - vintagechips/ttbasic_arduino: TOYOSHIKI Tiny BASIC for Arduino. Tested in Arduino Uno.

GitHub

 

Arduino用に作られているのでそのままコンパイル可能ですが、プログラム領域256byteではASCIIART.BASが入らないので512に変更しました。

#define SIZE_LIST 512 //List buffer size

最適化オプションが"Smallest (-Os default)"だと2kBほどサイズオーバーになり、"smallest (-Os) with LTO"だと16kB以内に収まりました。しかし、with LTOの方だと通信周りがちゃんと動かないようで、最初の1文字"T"が出て止まってしまいました。通信周りを直すか、別の開発環境を使えばうまくいくような気もしますが、とりあえずASCIIART.BASを走らせることを優先しようと思い、インタプリタの機能を削って小さくすることにしました。

やったことは、

  1. エラーメッセージの文字数削減
  2. ASCIIART.BASで使っていない機能を削る

です。

"2."の方はかなり乱暴で、配列、INPUT、GOSUB、RETURN、STOP、REM、RND、ABSを削りました。

これでやっと、

Sketch uses 16184 bytes (98%) of program storage space. Maximum is 16384 bytes.

Global variables use 1512 bytes (73%) of dynamic memory, leaving 536 bytes for local variables. Maximum is 2048 bytes.

に収まって無事起動。Serial.read, Serial.available周りに修正が必要かと思っていたのですが何も変更せずに動作しました。 ASCIIART.BASの方は、以前にFairchild F8 Family (F3850)Zilog Z8671 BASIC/Debug用に作った整数型BASIC用のプログラムをくっつけて作りました。

10 F=50
20 FOR Y=-12 TO 12
30 FOR X=-39 TO 39
40 C=X*229/100
50 D=Y*416/100
60 A=C; B=D
70 I=0
90 Q=B/F; S=B-Q*F
100 T=(A*A-B*B)/F+C
110 B=2*(A*Q+A*S/F)+D
120 A=T
130 P=A/F; Q=B/F
140 IF (P*P+Q*Q)>4 GOTO 200
150 I=I+1;IF I<=15 GOTO 90
160 PRINT " ",
170 GOTO 300
200 IF I<10 PRINT I,; GOTO 300
210 IF I=10 PRINT "A",; GOTO 300
220 IF I=11 PRINT "B",; GOTO 300
230 IF I=12 PRINT "C",; GOTO 300
240 IF I=13 PRINT "D",; GOTO 300
250 IF I=14 PRINT "E",; GOTO 300
260 IF I=15 PRINT "F",
300 NEXT X
310 PRINT
320 NEXT Y

動作している様子です。8秒です。さすが21世紀のマイコンは速いですね。

今回は動作させることを最優先にしたので機能を削るという乱暴な方法で実装しましたが、おそらくライブラリが余計なメモリを食っているような気がするので、開発環境を精査すれば16KBもあればフルセットで実装できると思います。

※追記(2023/6/30)

Serial関連のライブラリがかなりメモリを食ってる感じなのでそこを自分で書き直すのが良さそうかも。

※追記(2023/7/1)

メーカー推奨の開発環境MounRiver Studioだとフルセットでもオブジェクトサイズ9KB以下に収まりました。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その17) 周辺回路を現代の部品で作ってみる(TangNano編)

2023-06-14 18:03:16 | マイコン(4004)
4004の周辺回路を現代の部品で作る試みの第3弾、TangNano編です。やっと完成したのでブログにまとめることにしたのですが、かれこれ1ヶ月以上前からやっていて、最初の頃のことは自分でもすっかり忘れているので、twitterで自分のツイートを読み返しながら書きます。トラブったことや部品選定の話をとりとめもなく書くだけなので、技術的な話はGitHubの回路図やソースを見てもらう方がいいと思います。
GitHub - ryomuk/tangnano-MCS4memory: Memory system for Intel 4004 using Tang Nano 20K

GitHub - ryomuk/tangnano-MCS4memory: Memory system for Intel 4004 using Tang Nano 20K

Memory system for Intel 4004 using Tang Nano 20K. Contribute to ryomuk/tangnano-MCS4memory development by creating an account on GitHub.

GitHub

 


Tang Nanoというのは、Gowin SemiconductorのFPGAを使ってSipeedが製造販売しているFPGA評価ボードです。
当初Tang Nano 9Kというボードで試しており、64KBのメモリを実装するにはリソースがギリギリ足りないので実機と完全コンパチには出来ず、ブレッドボードでVTLを走らせるところまで試してさてどうしたものかと思っていたのですが、つい先日20Kという新製品が発売され、64KBのメモリを実装できるようになり、実機と完全互換のものが作れました。
9Kでブレッドボードで試していたときの写真がこれ。


この実験をしているときに、3.3V→15Vのレベル変換IC(CD40109)のGNDを継ぎ忘れていたせいでTangNano9Kを1つ壊してしまいました。40109にGNDを継げずに15Vと3.3Vだけ継げて検証実験をしたところ、入力端子(TangNano側の出力端子)に4.2Vの電圧が発生。


結構な時間こんな状態が続いていたのでまあ壊れるのも納得です。で、すぐに予備の9Kを2つ秋月に発注したのですが、なんとその日の夜にSipeedがtwitterで新製品の20Kの発売開始を発表。
とりあえず20Kの方もコントローラー付属のゲームキット1つとピンヘッダー無しを2つ注文しました。巷では5月25日頃に到着の報告を見かけたのですが、どうやらゲームキットもいっしょに注文したのがまずかったようで、発送が遅れて6月1日にやっと届きました。(しかも1個足りなくて、再送してもらいました。)


初物なのでいろいろトラブルがありました。主なのは2つかな。
1つ目はpin25とpin26がデータシートで逆に描かれているという問題。pin25に継げたスイッチが全く反応しないので何がおかしいのか調べていたらどうやらpin25とpin26が逆の様子。これは6月8日のアップデートで修正されました。
2つ目はpin75から信号が出ない、信号入力もできないという問題。当初全く動かないので、twitterでSipeed宛てにpin75が動かないよ~と下記画像を付けてツイートしてみました。

いろいろ調べているうちに、なぜか手動で信号をGNDと+3Vに切り替えると0、1はちゃんと入力できることがわかり、これはもしかしてと周波数を変えて試してみたところ、高い周波数がカットされているらしいという現象を見つけて追加でツイート。

これに対して別の人から、pin75はコンデンサにつながってるとのリプライがあり、原因がわかりました。

Sipeedからも、そのcapを外せばいいというツイートがあったので外して試してみたところ無事直りました。


pin75が使えないと基板作り直しだなあと思っていたので作り直さずに済んで良かったです。(今は未使用のCMRAM1_nの信号につなげているので実害は無いのですが気持ちが悪いので。)
同様の信号がpin51にもあるのですが、こちらのコンデンサはパターンだけあって実装されていません。pin75のコンデンサも次期ロットあたりから外されるのかな。

今回の試作でもうひとつトラブったのが電源周りの話です。(こちらはTang Nanoとは無関係な話です。)
周辺回路をほとんどTangNanoに入れてしまったので、15Vの電源が必要なのは4004とクロックドライバだけになります。4004のデータシートを見ると、平均消費電流は最大40mA、絶対定格で1Wとあり、クロックも4004にしか供給されないので、多く見積っても全部で50mAもあれば良さそうです。
5V→15V、1WのDCDCでいけるだろうと思ってブレッドボードで試していたのですが、電圧が14Vぐらいまでしか上がらないという謎な現象に遭遇しました。
3WのDCDCにとりかえて電流を測定してみたところ、なんと最初に90mAぐらい流れています。

15V1Wは67mAなので、これは過負荷です。14Vまでしか上がらないのも納得。
これが1分ほどで想定通りの33mAぐらいに落ちつきます。

最初は温度の問題かと思ったのですが、電源を入れなおすとまた90mAぐらい流れるので温度の問題では無さそう。4004とクロックドライバを疑って個別に計測してみたのですが特に問題無し。
CMOSゲートの4049を測ってみたところなんと1つあたり30mA流れていることが判明。これが犯人でした。いや、犯人は私なんですけどね・・・
4049の未使用入力をオープンにしていたのが原因でした。基板を作るときはちゃんとVCCかGNDに継げますが、ブレッドボードでは面倒なのでサボっていました。ごめんなさい。

電源がらみでもう1つ。DC/DCの選定にあたり、秋月で売ってるMINMAXにするか、ワールドワイドで入手性がいいRECOMのどちらにするか試してから決めようと思っていろいろ買ってみたのですが、RECOMの3WのRI3-0515Sがばかに電圧高めに出るなあと思っていたところ、DC/DCにはregulatedとunregulatedがあるということに気がつきました。

全部regulatedだと思ってたのですが、
MINMAXは1WのMAUはunregulatedで3WのMCWIはregulated。RECOMもROEやRI3はunregulated。regulatedが欲しければRS3なのでした。
3Wのunregulatedを0.5Wぐらいで使うと電圧は高くなってしまうというのは仕様通りの挙動。いろいろ買って試してみてやっと気がつきました。

1Wを0.5Wぐらいで使うのは丁度いいようなので、今回はワールドワイドで入手性のいいRECOMのROE-0515Sを採用することにしました。

最後に部品選定の話をもう1つ。クロックドライバについてです。
昔の4004の回路では、DS0026というドライバが使われていました。若松でしか売ってないレベルの昔の部品です。現行品を使う方がいいかなと思い、teensyやPIC版では秋月で売ってるMicrochipのMCP1402に変えてみたのですが、やっぱりSOT23+変換基板というのは美しくないのでいろいろ調べた結果、同じくMicrochipのMIC4427というのがDIPの現行品らしいのでこれを使うことにしました。


これについて調べてたときに、Microchipのこの表を見て途方に暮れていたのですが、


TIが作ってくれたこの表に助けられました。


これに限らず、TIの資料は本当にわかりやすいです。

で、なんやかんやで完成した基板がこれ。

部品のほとんどはレベル変換用IC(15V←→3.3V, 15Vクロックドライバ, UART用5V耐圧バッファ)です.

ほとんど昔の部品で作った基板はこちら。この赤で囲んだ部分が全部Tang Nanoに入ったことになります。


一定の成果が得られたので、4004の周辺回路を現代の部品で作る試みはひとまずこれでひとくぎりにします。当たり前ですが、ハードウェアをエミュレートするのはCやアセンブラよりverilogの方が簡単でした。
次は何をやろうかな。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その16) 周辺回路を現代の部品で作ってみる(EMUZ80(PIC)編)

2023-05-24 13:50:42 | マイコン(4004)
周辺回路を現代の部品で作る試みの第2弾、EMUZ80+4004メザニンボードです。
EMUZ80というのは電脳伝説さん設計によるワンボードマイコンです。詳細はこちら(EMUZ80が完成)。Z80の周辺回路(ROM, RAM, UART, クロック等)を全てPIC1つに実装して、CPU+PICという最少構成かつ低コストなシステムを実現しようという設計思想のもとに作られています。
レトロマイコン界隈でちょっとしたブームになっているようで、Twitterでは高速化したりCPUを別のもの(6502, COSMAC(1802), 68030等々)に改装したりしているのを見かけます。面白そうなのでその流れに乗って真似してみることにしました。

Z80やら68030が動いてるんだから4004なんて余裕だろう、と思っていたのですがそれがまず間違いでした。電脳伝説さんによる解説記事「EMUZ80の設計と製作」(私はboothでカラー版を購入しました。白黒より読み易いし読んでて楽しいです。)によると、どうやらMREQ_nを受けてからWAIT_nを出して待たせるところが割と重要なポイントのようで、WAIT信号の無い4004ではその手法が使えません。
COSMACはどうやったんだろうと思ってググったところ、やはりCOSMACもWAITが無いので200~300kHzが限界(EMU1802-mini の高速化に挑戦しました@COSMAC研究会)とのことのようでした。750kHzで動かすのは難しそうです。

要求条件を整理します。4004のインストラクション実行サイクルとデータアクセスの詳細なタイミングはこちら。


バスは時分割されていて、A1~A3の3サイクルで4bit×3の12bitのアドレスがCPUからメモリに渡され、続くM1,M2サイクルでデータがメモリからCPUに渡されます。
要は、A1~A3で受けたアドレスのROMデータを1350nsで用意してM1サイクル出力すれば良いわけです。あれ、意外に出来そうじゃん。と思ったのですが、計算してみるとかなり厳しいです。
PIC18F47Q43はクロックは64MHzとはいうものの、1命令4クロックなので1命令あたり最短62.5nsかかります。1350ns/62.5ns=21.6 なので、21命令しか使えません。
データシートによると、"Fixed interrupt latency of three instruction cycles"とあるので、割り込みは3サイクルなのですが、割り込みルーチン内でA1~X3の8種のサイクルの分岐をしているとそれだけで20命令は余裕で超えてしまうので、割り込みを使うのはやめました。
とにかく命令数を少なくするという方針で考えてみた結果、SYNCでA1サイクルを開始して、クロックをポーリングしてA1, A2, A3, M1, M2, X1, X2, X3, SYNC待ちとサイクルを進めて行きながら各サイクルの処理を実行するという方式にしました。
とりあえず書いてみたところ、4004の下限クロックの500kHzでも動かず、データシート仕様以下の300kHzまで下げてやっと動きました。動画がこちら。

コンパイラの出力を見たところ、romの配列を読むためのアドレス計算で結構無駄なことをしてそうなのでそのあたりを直すことにしました。
まず、EMUZ80の高速化事例を調べていたところ、
const byte rom[ROM_SIZE]  __at(0x10000) = {...
のように、配列をキリの良い固定アドレスに配置する手法があったのでそれを真似しました。
また、インラインアセンブラは必須のようなのでデータシートを読んでPICのインストラクションを調べたり、コンパイラの出力を見ながら見様見真似で書いてみました。
コンパイラの出力を見たところ、ポーリングはポート値による分岐命令(btfsc)とジャンプ命令(goto)の2命令なので、理想的には125nsの遅延なのですが、オシロで観測したところ、どうもクロックエッジから250nsぐらいの遅延がありそうでした。2相クロックのHが960ns, Lが384nsなので、250nsというのはちょっと無視できない遅延です。
なのでちょっと汚いのですが、CLK2↑で読むべきアドレス値はCLK2↓で読んだりしています。また、A3→M1がクリティカルパスなのですが、そこではCLK1↑をトリガにしてNOPで時間調整して読むとか突貫工事で作りました。
主要なループはこんな感じ。
※ここにソースの一部を貼ろうとしたのですが、うまく貼れないのでmain.cを見て下さい。
実は、まだ^(0+1280)とか,cとか付いているおまじないが理解できていません。RAM bankに関係ありそうで、データシートでa={0,1}とか書かれているRAM access bitみたいなんだけど、なぜ"c"なのかが謎。

話が前後しますが、2相クロック生成の話も書いておきます。Teensy, PIC, TangNanoの3つを試してみて、PICが一番簡単だったのは2相クロックのマスターになる5.185MHzのクロック生成です。NCO(Numerically Controlled Oscillator)なるモジュールを使って簡単に作れました。

しかし!そこから2相クロックにするのが大変でした。PWMはduty比は制御できたとしても位相制御が(そもそも出来るのかどうかすら)わからなかったので諦めました。TMRレジスタも違うよなあと思いながらいろいろ調べていたところ、EMUZ80ではWAIT信号の生成に使われていたCLC(Configurable Logic Cell)が使えそうなことに気がつきました。
4004の2相クロックは7進カウンタで作ります。CLCを3つJK-FFにしてカウンタを作りました。ただこのFF、クリアが非同期だったのでクリア条件とクリアを同期化するためのFFにCLCを2つ追加しました。さらにCLK1, CLK2の生成に2つ使って、合計7個のCLCで2相クロックを作ることができました。CLCの1つがこれ。こんな感じのものを7個、マウスをポチポチしながら作るのは結構面倒くさかった。


あまり納得のいく作り方ではなかったのですが、とりあえずブレッドボード上でLチカが740kHzで動くようになりました。

余裕があればRAMとUARTも載せたかったのですが、PICはもう性能的に無理だと感じたので諦めました。
CPU以外をPIC1個に載せるというEMUZ80の設計思想を実現できなかったのは残念ではありますが、"EMUZ80-4004"を名乗るために、EMUZ80に接続できる基板を作ることにしました。
回路図はこんな感じです。4002は電卓を動かすのに最低限必要な2つです。5V→15VのDCDCのおかげで電源はEMUZ80から供給する5Vで動きます。-15VをGNDにしたので、通信部分はレベル変換用のCMOSで代用可能だと後から気がついたのですが、前と同じフォトカプラ2つを使っています。



回路図通り、左側にレベル変換、右にCPUとRAMを配置する方が配線が楽なのですが、CPUは本来のZ80の位置に置く方が絵的に美しいので頑張って配線しました。コネクタからの信号が一旦右に行ってから左のCPUに戻り、CPUからの信号も右に行ってから左に戻るということになるので結構大変でした。



今回は10cm×10cm以下だったこともあり、配送方法にOCSのネコポスが選べたので送料込みで$3.5。激安です。$7クーポンを使ってMCLZ8の4層基板もタダで作ってもらっちゃいました。


さっそく部品を載せて動作確認。メモリ(4002)が2つしか無いので電卓プログラムは若干の修正が必要でした。ついでに起動画面のメッセージもちょっと修正しました。


クロックドライバに秋月で入手可能なSOT23パッケージのMPT1402をDIP変換基板に載せて使ったのですが、ちょっと格好悪いですね。次に作るときはDIPで2回路入っているMIC4427にしようと思っています。あと、フォトカプラも普通のCMOSのバッファに変更かな。

電卓が動いた動画がこちらです。


とりあえず作ってみましたが、PICはもう使わないかなあというのが正直な感想です。PICはCPUコアそのものに仕事をさせるんじゃなくて、周辺モジュールを働かせて何かを作るためのものだと感じました。あと、開発環境のMPLAB X IDE とMCCも高機能すぎて難しかった。

GitHubに回路図、基板製造データ、プログラム等一式を置いてあります。
GitHub - ryomuk/EMUZ80-4004: 4004 mezzanine board for EMUZ80

GitHub - ryomuk/EMUZ80-4004: 4004 mezzanine board for EMUZ80

4004 mezzanine board for EMUZ80. Contribute to ryomuk/EMUZ80-4004 development by creating an account on GitHub.

GitHub

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その15) 周辺回路を現代の部品で作ってみる(Teensy編)

2023-05-10 11:08:50 | マイコン(4004)
できるだけ当時の部品で作るという当初の目標は達成できたので、次は現代の部品を使ったらどうなるかを試してみました。候補としては、
(1)Teensy 4.1+4004
(2)EMUZ80+4004メザニンボード
(3)TangNano9K+4004
あたりを考えたのですが。まずはread4001(Intel 4004 (その5) Mask ROM 4001を読んでみる)で4001を読んだ実績があるTeensy4.1で試してみることにしました。

read4001のときは3.3V→15Vの変換はFETの2N7000を使っていたのですが、これでいいのか若干気になっていたので別の部品も探してみました。見つけたのがこちら。
CD40109B Low-to-High Voltage Level Shifter (datasheet)

実際の信号で2N7000と比べてみました。
上から、入力(3.3Vパルス)、40109、2N7000(4.7kプルアップ)です。(100ns/div)

40109は立ち下がりの遅延が2N7000の倍の100nsぐらいありますが、それ以外はかなり良い特性です。出力制御(OE)も備えているのでこれを採用することにしました。

この他に、CD4504B(Voltage-Level Shifter)も候補にあったのですが、全ての点で40109の方が勝っていたので不採用です。
上から、入力(3.3Vパルス)、40109、4504。(50ns/div)


次に15V→3.3Vです。4004のデータシートによると、HはVss-0.5~Vss(今回の場合は+14.5~15V)、LはVss-12~Vss-6.5V(+3~8.5V)です。実際の信号を観測してみると、Lは+3~+5Vぐらい。

read4001の時と同じように、1段目をVcc=15VのCMOSで受けて、2段目をVcc=3.3VのCMOSで受ければ良さそうです。1段目は閾値を高くするためにVcc=15Vにする必要があるので標準CMOS、2段目はVcc=3.3Vでいいので高速な74HC4049(入力耐圧は+15V)を使うことにしました。

クロックドライバの3.3V→15Vは秋月で売っているMicrochipのMCP1402(datasheet)を使ってみることにしました。最初は5.185MHzのクロック源(MCLK)を外部に用意して、ソフトで2相クロック(CLK1, CLK2)を生成するという構成を試したのですが、Teensyの内部カウンタ(600MHz)でわりと安定したMCLKまで作れてしまいました。

完成した回路図がこちら。


次にプログラムです。
速度が要求されるので、ポートレジスタ直叩きとかやる必要があるかなあ、面倒だなあと思っていたのですが、digitalWriteの速度を測ってみたところ驚愕な速さだったのでデータバスを1ビットづつ読み書きするというお子様的なプログラミングで済ませてしまいました。
こんな感じのコードで、
#define digitalWrite digitalWriteFast
  digitalWrite(DOUT0, data & bit(0));
  digitalWrite(DOUT1, data & bit(1));
  digitalWrite(DOUT2, data & bit(2));
  digitalWrite(DOUT3, data & bit(3));

この速度。Teensy4.1の性能恐るべしです。(これ5ns/divですよ)



プログラムはクロックを生成しながら4004の命令サイクルにあわせてバスを読み書きするだけなので特筆すべきことは無いかな。

Lチカが動きました。


回路図やプログラムはGitHubに置きました。
GitHub - ryomuk/Teensy-4001: Intel 4001 (MCS-4 mask ROM) emulator using Teensy 4.1

GitHub - ryomuk/Teensy-4001: Intel 4001 (MCS-4 mask ROM) emulator using Teensy 4.1

Intel 4001 (MCS-4 mask ROM) emulator using Teensy 4.1 - GitHub - ryomuk/Teensy-4001: Intel 4001 (MCS-4 mask ROM) emulator using Teensy 4.1

GitHub

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その14) メモリ64KB化とプリント基板作成

2023-04-12 13:32:02 | マイコン(4004)
8080エミュレータで言語処理系等まで走るようになると、4KBというメモリでは少し窮屈なので増やしてみることにしました。4KBのときは4289からのアドレス8bitに4002からのバンクアドレス4bitで12bitでしたが、バンクアドレスを8bitににして、256byte×256バンクの64KBにします。実際には256byteのうち2byteをメモリ読み込みルーチンに使われてしまうので、使える領域は254byte×256バンクで63.5KBです。
64K×4bit=256Kbitのメモリを使いたかったのですが、探しても売っているところを見つけられませんでした。ヤフオクで256K×4bit=1MbitのHM624256が2個560円で売られていたのでそれを使うことにしましたが、今考えると同じ1Mでも128Kx8bitのHM628128の方が格段に入手性が良いのでそちらにすれば良かったと思っています。DIP以外のパッケージで良ければ今でも秋月で同等品が入手できるようなので。
ブレッドボードで改造するのは面倒だったのと、そろそろ基板を作りたいと思っていたところなので基板を作ることにしました。10cm×10cmを超えると急激に高くなると思っていたのですが、JLCPCBだとかなり安く作れるというのもブレッドボードでの確認を省略する動機になりました。

基板の大きさは秋月のユニバーサル基板に合わせるといいかと思ったのですが、A基板(155mm×114mm)でもちょっと小さく、その倍だと大きすぎるので、mmでもインチでもキリがいい190.5mm×127.0mmにしました。
とりあえずこの大きさに載るかどうか確認するために適当に並べたのがこちら。

載ることは載りましたが配線がスパゲッティになりました。4002のポートから出ているLEDを一箇所にまとめるために結構配線が増えたのが主な原因。
配線を綺麗にすることを念頭において引き直したのがこちら。

あまり違いが無いようにも見えますが、だいぶ良くなったと思います。
実は、初版(rev2.0)を発注した後にいくつか間違いを見つけてしまい、しかも送料をケチってしまったせいでなかなか届かず、我慢できなくなって初版が到着する前に修正版(rev.2.1)を発注するということをしてしまいました。
で、昨日到着したrev.2.1がこちら。(rev.2.0は4日前に届いたのですがお蔵入りです。)

部品を載せたのが冒頭の写真です。ホワイトセラミックのC4289は先日入手したものです。運良く入手できましたが、4289は4004より入手困難になっているような気がします。
電源を入れてみたところ無事動作しました。新規設計部分の64KBのRAMもメモリテストプログラムで正常動作が確認できました。


ブレッドボード版と並べたのがこちら。半分ぐらいの面積になりました。


回路図やガーバーファイル一式をGitHubに置きました。製作に必要な情報は全て置いてあるので、部品さえあれば同じものが作れます。
GitHub - ryomuk/emu8080on4004: Intel 8080 Emulator on 4004 Evaluation Board

GitHub - ryomuk/emu8080on4004: Intel 8080 Emulator on 4004 Evaluation Board

Intel 8080 Emulator on 4004 Evaluation Board. Contribute to ryomuk/emu8080on4004 development by creating an account on GitHub.

GitHub

 


あと、先日Twitterで教えてもらって驚いたのですが、HACKADAYの記事になったようです。
The 4004 Upgrade You’ve Been Waiting For

The 4004 Upgrade You’ve Been Waiting For

You know how it is. You have an older computer, and you can’t run the latest software on it. Time to upgrade, right? Well, if you have been in this situati...

Hackaday

 


HACKADAY.IOのアカウントを作ったので、そちらにも書いてみようかと思っています。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Intel 4004 (その13) 8080エミュレータを作ってみる

2023-03-22 11:22:20 | マイコン(4004)
電卓やVTLインタプリタを書いてみて、ずいぶん4004のアセンブラに慣れてきました。とはいえ、いろいろなものを4004のアセンブラで一から作るのは面倒です。8080のエミュレータを作ってしまえばモニターなり言語処理系なり、8080の膨大なソフトウェアを走らせることができるのではないかと思い、エミュレータを作ってみることにしました。CPUの動作なんて命令コードで分岐してレジスタの値を書き替えるだけなんだから1日か2日で作れるんじゃないかと高を括っていたのですが、意外に大変で、4004のモニターもメモリ読み書き周りを手直ししたり、8080のプログラム入力用にIntel HEXを読み込めるようにしたりという改造もあったので結局1週間ほどかかりました。
実験機の4004のプログラムに使えるROM領域は3.75KBあります、モニター周りの機能追加や、レジスタ操作、PUSH/POP、I/O、論理メモリ空間アクセス、等々のルーチンで2KB使ってしまうので、エミュレータ用に使えるメモリは1.75KBしかありません。
当初のもくろみでは、4004の間接ジャンプ命令で命令コード毎に分岐して分岐先に各命令の処理を書けば簡単だと思っていたのですが、さすがにそれでは無駄が多くてメモリが足りなくなるので、もう少し真面目にやることにしました。

8080の命令コード表を見ると、40H~BFHの128命令はかなり規則的な作りになっています。その外側もある程度は規則的なのですが、かなり雑多です。

01H~3FHとC0H~FFHをJIN命令(間接ジャンプ)で分岐テーブルを使って分岐させて命令毎の処理、40H~7FHはMOV命令なのでSRCレジスタとDSTレジスタをデコードしてMOVの共通ルーチン、80H~BFHは、SRCレジスタをデコードして、演算内容を分岐テーブルで分岐させて演算毎の共通ルーチンで処理、というような作りにしました。
MOV命令は01000000+DDD000+SSSという構造になっているので、SRCとDSTをデコードしてあとは共通ルーチンに任せるということです。

ちなみにHLT(=76H)のコードはMOV M, Mに相当するのですが、MOVのルーチンで判別してHLTの処理に飛ばしました。

いくつか実装をサボったものもあります。
まず、演算結果のbitの偶奇を示すPフラグです。ハードウェアで実装したら簡単そうですが、ソフトウェアで実装するのは結構コストがかかります。また、フラグの使用の有無にかかわらず演算ごとに計算するのはかなりの無駄です。このフラグを使ったプログラムは滅多に無いと思われるので実装するのをやめました。
次にDAA、10進数の補正命令です。4bit目のキャリーであるACフラグ(Auxiliary Carry, NECのマニュアルだとハーフキャリーやCY4と表記されていたりします。)を用意しておく必要があるのですが、現在の実装では面倒なのでサボりました。あと、割り込み関連のDI、EIも割り込み自体が無いので省略。(※2023/4/3追記, DAA命令に関する記述について修正しました。)
IN、OUTはシリアルポートへの入出力になっています。INでの入力は4004のソフトウェアUARTのGETCHARルーチンに飛ぶので、入力があるまで止まってしまうのですが、これを回避するにはハードウェア的な機能追加が必要なので今のところはあきらめています。(BASICで停止のためのCtrl-Cのチェックとかができないのでなんとかしたいです。)

意外に面倒だったのが論理演算です。4004にはAND, OR, XORなどの論理演算用の命令がありません。MCS-4 Assembly Language Programming Manualには4bitのANDルーチンの例(下記)が載っているのですが、ちょっと難解で理解できなかったので自前で書きました。


自前で書いたのがこちら。ループを使わずに1bitづつ調べているのでかなり長くなっています。

;;;---------------------------------------------------------------------------
;;; AND_R6_R7
;;; R6 = R6 & R7
;;;---------------------------------------------------------------------------
AND_R6_R7:
        CLB
        LD R7
        RAR
        JCN C, AND67_L1 ; jump if R7.bit0==1
        LD R6
        RAR
        CLC
        RAL
        XCH R6          ; R6.bit0=0
AND67_L1:        LD R7
        RAR
        RAR
        JCN C, AND67_L2 ; jump if R7.bit1==1
        LD R6
        RAR
        RAR
        CLC
        RAL
        RAL
        XCH R6          ; R6.bit1=0
AND67_L2:
        LD R7
        RAL
        RAL
        JCN C, AND67_L3 ; jump if R7.bit2==1
        LD R6
        RAL
        RAL
        CLC
        RAR
        RAR
        XCH R6          ; R6.bit2=0
AND67_L3:
        LD R7
        RAL
        JCN C, AND67_L4 ; jump if R7.bit3==1
        LD R6
        RAL
        CLC
        RAR
        XCH R6          ; R6.bit3=0
AND67_L4:
        BBL 0

これは4bitのレジスタの論理積なので、8bitのレジスタの演算には次のように2回呼ぶ必要があります。

;;;---------------------------------------------------------------------------
;;; AND_P1_P2
;;; P1 = P1 & P2
;;;---------------------------------------------------------------------------
AND_P1_P2:
        LD P1_LO
        XCH R6
        LD P2_LO
        XCH R7
        JMS AND_R6_R7
        LD R6
        XCH P1_LO

        LD P1_HI
        XCH R6
        LD P2_HI
        XCH R7
        JMS AND_R6_R7
        LD R6
        XCH P1_HI
        BBL 0

8080で1命令(ANA)で済む1バイトの論理積の計算にこれだけのコードが必要になります。ORとXORも同様です。メモリが本当に厳しくなったら工夫して短くするところですが、今回はなんとか足りたのでこの実装で済ませました。

一通り書けたところで、動作確認です。全ての命令について、ステップ実行させながらレジスタ、フラグ、PC、SP、メモリの内容が仕様通りに動いているかを確認します。自動のテストプログラムを書くという方法もありますが、そうするとテストプログラム自体のデバッグも必要になるという無間地獄に陥るのでテストプログラム自体は単純な命令の羅列にして、結果を目視で確認するという手段をとりました。テストはかなり効果的で山ほどバグが見つかりました。


全ての命令で一応期待通りの動きが確認できたので、次は大規模なプログラムを走らせてみます。
ソースが入手可能で改変も可能なPalo Alto Tiny BASIC を試してみることにしました。ソースはhttps://www.autometer.de/unix4fun/z80pack/ftp/altair/から入手。

Pフラグ、DAA命令が使われていないことを確認。端末への入出力は、IN 1、OUT 1でデータ、IN 0がデータ有無を示す制御フラグのようだったので、エミュレータ側のIN、OUT命令をそれ用に設定。
アセンブラの"SHR"を">>"に、"AND"を"&"に修正、スタックポインタとメモリ領域のアドレスを修正したらすんなりアセンブルできてHEXファイルが生成できました。
実機にロードして実行してみたところ、"OK"のプロンプトが出てくれました。PRINT 123+234も正常。
しかしPRINT文やLIST出力の1行ごとに入力待ちになって停止してしまいます。
Tiny BASICでは、Ctrl-Cで止めるために入力のチェックがあり、入力が無ければ通過するのですが、4004実験機はGETCHARは入力があるまでそこで止まってしまうのでした。このあたりはハードウェアの改修も必要になるので、とりあえずの対処として、Tiny BASIC側でコマンド入力時以外のIN数カ所をコメントアウトして済ませることにしました。無限ループ時に止める手段が無くなるのですが仕方ありません。
以上の対処で動くようになった動画がこちら。ちゃんと動いているのは感動ですが、速度は思っていた以上に遅く、マンデルブロ集合の計算はあきらめました。



ソースコードと実験機の回路図はGitHubに置きました。
GitHub - ryomuk/emu8080on4004: Intel 8080 Emulator on 4004 Evaluation Board

GitHub - ryomuk/emu8080on4004: Intel 8080 Emulator on 4004 Evaluation Board

Intel 8080 Emulator on 4004 Evaluation Board. Contribute to ryomuk/emu8080on4004 development by creating an account on GitHub.

GitHub

 
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする