Z80で遊ぶ

先日basic で動かしたマンデルブロー集合の描画でbasic が遅い、ということを痛感して、もう少し速く動かしたときどの程度差があるのかな、ということで、アセンブラでロジックを記述して動かそうと思いました。

問題は、このプログラム、浮動小数点演算を使っているので、ライブラリが必要ということ。で、最初はbasic の中の浮動小数点演算ルーチンを使わせてもらうことを考えていたのですが、まずもって、アセンブラで記述したプログラムをpokeなどでメモリに転送してusr関数で実行するということをやってくれるbasic のプログラムを作成するのが面倒です。

それでだんだん当初の趣旨から外れて、浮動小数点ライブラリを簡単に使えるプログラミングスタイルについてしらべてました。Z80のアセンブラもいろいろあり、cp/m で動作するもの、windows, linuxなどで動作するもの、Web(online)で動作するもの、いろいろありますが、アセンブラはニモニックはともかくも、疑似命令や、引数の計算ルールが様々で、なかなかまとまって、どこかから持ってきた演算ライブラリを引いて、すっきり実行できるものがありません。なかなか悩ましいところです。

それで、Cコンパイラも視野に調べたのですが、こちらも、cp/m で動作するBDS-Cがなかなか、と思いましたがこちらは浮動小数点がサポートされておらず、結局hi-tech-cぐらいでしょうか?Arduino的に簡単にソースコードを書いてZ80で何かするのに一番いいのは。

そんなことを調べて時間をずいぶんつぶしてしまいました。

現在のところ、CP/M program EXEcutor for Win32 を使い、hi-tech c を動かし、下記のソースをコンパイル、.com ファイルを前に作ったZ80-MBCがすでにCP/M Ver 2.2が動いているのでそちらのvirtual disk に.com ファイルを書き込んで動作させる、ということはできました。

Cのソースは、下記のような、もともとのBASICのプログラムそのまま、みたいな感じですが、浮動小数点ライブラリをリンクしてコンパイルしました。

#include <stdio.h>
#include <math.h>
main ()
{
	float ca,cb;
	int i;
	int y,x;
	float t,a,b;
	for (y=-12;y<=12;y++) {
		for (x=-39;x<=39;x++) {
			ca=x*0.0458;
			cb=y*0.08333;
			a=ca;
			b=cb;
			for (i=0;i<=15;i++) {
				t=a*a-b*b+ca;
				b=2.0*a*b+cb;
				a=t;
				if (a*a+b*b>4.0) goto l200;
			}
			printf(" ");
			goto l210;
		l200:
			if (i>9) i=i+7;
			printf("%c",48+i);
		l210:
			continue;
		}
	printf("\n");
	}
}

Z80-MBCで実行してみたところ、82秒ほどで絵を作成できました。CPUは4MHz でこれは動いているので、同じ条件で比較はできていませんが、1桁はいかないけど、ぐらい高速になる、と考えてよいかな。

これがまあまあ、調子よいので、SBCZ80の方も、CP/MのシリアルI/Oを実装してCP/Mのバイナリを動かすのが楽にいろいろできる方法かな、と思っているところです。

コンパイルしたバイナリーをPCでCP/M program executor で動かすと、最初はちょっと時間(1秒ぐらい?)かかるんですが、2回目からは、どこかのキャッシュが働くのか一瞬で結果が出ます。どういう仕組みが背景にあるのかななどちょっと興味深く思いました。

assembler やhi-tech-cでコンパイルしたバイナリ(COMファイル)をCP/Mに転送するのに毎回virtual disk を作ってたら日が暮れてしまうので、シリアルで転送する方法を試行錯誤していました。

xmodem がいいのではということで、いろいろと試していたんですが、うまくいっておらず、今のところ、.com をpython bin2hex.py –offset=0x100 hoge.COM > hoge.HEXのようにhexに変換し、pip hoge.HEX=con: でcp/m のディスクに保存し、 load hoge.HEXでcp/m のディスクにCOMファイルを作成するような確実だけど回りくどい方法を取っています。xmodemが上手くいかないのは、タイミングがPC側とあってないのかも。

SBCZ80をつくる

相変わらず台風で強い雨が降ってます。外に出かける気持ちになれないので、はんだ付けで遊びました。

SBCZ80はZ80をCPUとしたワンボードマイコンです。DRAMをワンボードマイコンボードながら使っているのと、シリアルIOに8251 ではなくZ80 SIOを使っているのが特徴です。

ということで、こちらもDRAMのM5M4416の入手に時間がかかった(結局eBay中華)のですが、今日形にしてみました。

CPUは由緒正しいSHARPのLH0080です。SIOはCMOSの10MHz Z80も入手してあるので、Z80B SIO/2 を乗せてありますが、ノスタルジックに標準速(2.4576MHz)で動かします。周辺のTTLは全部CMOSです。HC04は由緒正しいTOSHIBAの部品箱の底に沈んでいたものを使いましたが、例によって昔のDIPICをかなり捨ててしまったため、結局ほかのICは購入する必要がありました。

ということで、電源ONで、いきなりBASICが動きました。終わり。というのも何なので、配布されているデータパックに収録されているマンデルブロ集合を書くプログラムを動かしてみました。

これを計算するのに8分9秒=489秒を要しました。

このデモは8ビットPCとBASICが使われていた時代にはよく見かけたものですが、最近は見ないので、今のマイコンってどれぐらいでこれを計算できるのかな、とふと思い、手元のArduino Megaで同じプログラムを特に工夫もせず書いて実行してみたところ、2秒程度でした。

もちろん、Arduinoはコンパイラーなのですが、圧倒的(?)な速度差です。コンパイラーを使っていることに加え、クロック速度は6.4倍異なり、AVRはRISCで1命令1クロック(?)に対してZ80は1命令4-20+クロックを要すことを考えると当然とは思いますが。。。

ということで、倍速で動かすよりは、標準速で遅さとほんのり温かいCPUでノスタルジーを感じるのがこれの正しい楽しみ方と思いました。まあ、本当のところは私になじみのあるZ80って、標準速より速いZ80A(MSXで採用)なのですが、Z80Aを買ってなくって、CMOSのCPUではノスタルジーにはどうしても浸れませんので。

SBCZ8をつくる

火星最接近したのに曇天続きで、今晩に至っては台風接近。ということで、SBCZ8を作りました。部品は、だいたいオレンジピコさん、多少秋月さんで揃えましたが、案の定28C16 (EEPROM)と 6116 (SRAM)が見つからず、eBayで買いましたので、ちょっと時間がかかりました。とはいえ、9月下旬に国内の部品、eBayで買ったのは中国から黄色い封筒で今日届きましたのでそれほど時間がかかるわけではありませんでした。

Z8はよく知らないので、ノスタルジックな感慨もなく、CMOS版のZ86C911を、他の部品もCMOSで揃えました。6116は由緒正しいHM6116ALP-3(でも中国)。28C16も由緒正しいAtmel AT28C16です(でも中国)。

とりあえず必要部品を並べてみる。抵抗ジャンク箱をあさるのがめんどくさく1/8W抵抗が混じってしまった。

はんだ付けするだけだから秋の夜中のはんだ付けセラピーで完成。

28C16にZilog BASIC/Debug (TINYBASIC+モニタみたいなもの?)を書き込みテスト。

案の定28C16の不良品混じり。しかしこんなこともあろうかと、5個セットを購入したので、OK. 6116に至っては10個入りを買いました。

書き込んだEEPROMをソケットにさしてPWRON。あっさりプロンプトが出て動作確認。

これ使って何かをするわけではないですが。。。

ただ、これってI/Oが簡単に使えてターミナルから対話型でぱちぱちポートの状態を変更できるという意味では,結構尊い感じです。今だとmicropythonがそれ?って感じ?

PSoC 手ならい

本当にメモ的だけど、Cypress PSoCのuniversal digital block(UDB)を使って何か作ってみようかと思ったので。学習の過程をメモする。

学習環境

秋月でPSOC 4200 prototyping kit (600円)を2枚購入。隣に置いてあったPSOC5LPが搭載されたキットは2000円弱と高いが、せっかくなのでこれも購入。

4200 prototyping kit はとっても安いが、USBから書き込むためには、ターゲットに uart経由で書き込むブートローダーが書き込まれている必要があり、結局SWDから書き込みツールが欲しくなるが、実はPSOC5LPのキットが高い書き込みツールの代わりになるそうで、買っておいてよかった。性能的にはPSOC5LPは私には無駄ですが。

PSoC Creator 4.2 をPCにインストールする。4200 prototyping kit に書き込まれているLチカ+UARTブートローダーのプログラムもダウンロードする。

こちらを参考に(というか指示通りに)、Lチカコードをいじくって、UARTブートローダーを使って書き込み(最初マニュアルでスイッチを押しながらUSBに接続してブートローダーに入れる必要があることを理解しておらず、悩む)、プログラムの動作を見る。

PWMのクロックを100kHz にしたりデューティーを変えたりしてLEDの調光をして遊ぶ。超簡単。

今日はここまで。

1枚目は書いたプログラムの動作を ロジアナで 観察しようと思うのでピンヘッダーを適当につけようかと思う。

micropython で遊ぶ

マイコンボード収集癖で溜まったボードにmicropython を入れて遊んでみました。

目的の半分は、python を使おうと思っているけど、具体的にやることがないと手が動かないからなかなか覚えられない。じゃあ、micropython でプログラムを実際に書くようなことをやればいいんじゃないかな?と思いました。

PCと装置の間をシリアル通信でやり取りして装置の状態をPCの画面でリアルタイムで表示するプログラム(よくあるやつ)を作ろう、と思ったんですが、休日の勉強を兼ねて、PCの方で動かすプログラムはpython + matplotlib + pySerialで作ろう。あー、でもpython 書けないなあ、それに装置のシミュレーターもないとシリアル通信の相手もないなあ、じゃあ、手元のマイコンボードでシリアル通信する装置のシミュレーターを作ろう。micropython をマイコンに入れてそれでシミュレーター作ったら簡単じゃね?

という、簡単なんだか回りくどいんだかわからないことを(休日なので)してみました。

いくつかハマったことなどもあるので、下記に書いておきます。

マイコンボードは引き出しで眠っていた、STMのNUCREO f446re とSTM32F429Iのdiscoveryボードを試しました。 最終的にはどちらも動きましたが、discoveryボードの方はbuild オプションを間違えていてしばらく悩んだ。

STM32F429I discovery 私のmicropython開発環境!

まずこちらのいうとおりに、手元のUbuntu linux にgit でmicropython の内容をclone しました。

micropython ディレクトリのREADMEにある通り、まず、mpy-cross をmake しておく必要、その他コンパイラーなどを入れておく必要、がありますが、そのうえで、ports/stm32/で

make BOARD=STM32F429DISC

(BOARD=のオプションはboardsの下にたくさんディレクトリがあるのでそれから選ぶ)

とかすると、firmware.hex などがbuild-STM32F429DISC の下にできますから、それをマイコンボードに書き込めばOK.私は、USBでマイコンボードをPCにつないでwindowsのst-link utility でさくっと書き込みました。

micropython が動作するとteraterm などでPCから接続してプログラミングするわけですが、ボーレートがすぐわかるところに書いていなくて難渋。115200 bps でした。

MicroPython v1.11-72-g9cebead on 2019-06-29; F429I-DISCO with STM32F429
Type "help()" for more information.
>>>

てな感じで、プロンプトが出ますが、ここでpython のプログラミングができるのは面白い。いろいろなやり方があると思いますが、PCのエディタでコードを書いておいて、micropython のターミナルでCtrl-Eでペーストモードに入り、書いたコードをpasteし、そのあとCtrll-Dで実行、やめるときはCtrl-Cというのが一番楽なやり方かな、と思います。プログラムの実行をCtrl-Cでいつでも止められるし、そのあと変数をREPLで書き出したりできるのもデバッガ内蔵という感じで非常にプログラミング学習が楽しいです。

micropython は、ドキュメントを読むと書いてありますが、最初にflash ディスクのboot.py を実行して、そのあとmain.py を実行するようになっているので、プログラムができたらboot.py, main.py の形で書き込んでおくのが良いのだと思いますが、基本的にはこういうので試すのはせいぜい100行程度のちいさなプログラムなので、paste and run の形が最も簡単にいろいろ試せて楽かなと思います。

boot.py, main.py に書き込むのは、ampy とかをpcにインストールしてコマンドプロンプトでやるのもよし、私は、試したなかでは、uPyCraft のようなwindows でのIDE環境がpythonのエディタとterminalが一緒になっていて、ペーストモードでの操作がスムーズなので、割とやりやすかった。main.py をエディタで書いていくんですが、マイコンのmain.py にダウンロードはせず、ターミナルからペーストしてコードを試す方法。

肝心のpython の方は、いつもCとかでダラダラ書く癖が抜けず、苦労しましたが、こんな感じで、PCからコマンドを受けては返すのを書いてみた。正直、これは、input文とprint文をただループさせればいいんですが、input待ちの間に処理をすることができるように、わざわざnon block serial input を書いてみたけど、もっとスマートなやり方があると思う。webでmicropythonの実践的な実装例を探したけど、見つからないんだよな・・・

from math import sin
led=pyb.LED(1)
buf=''
uart=pyb.repl_uart()

def input_nonblock(uart) :
 global buf
 s1=None
 if uart.any():
      s=uart.read()
      for c in s:
        if c==0x0d : 
          #print(str(buf))
          led.toggle()
          s1=str(buf)
          buf=''
        elif c==0x0a:
          pass
        else:
          buf=buf+chr(c)
      return s1
      
      
def update_vars():
  global lason, vlaser,itec,vtec, rtact,iphd,ain1,ain2,freq
  phase=((pyb.millis()/1000.)*freq*6.28)%6.28
  rtact=10000+sin(phase)*300

def init_vars():  
  global lason, vlaser,itec,vtec, rtact,iphd,ain1,ain2,freq
  freq=1.5
  lason=0
  vlaser=0
  itec=0
  vtec=0
  rtact=10000
  iphd=0
  ain1=0
  ain2=0

def show_prompt():
  print('>>',end='')

init_vars()
while True:
    s=input_nonblock(uart)
    if (s!=None):
      args = s.split(' ')
      if (args[0]=='status'): 
        update_vars()
        print()
        print(lason,vlaser,itec,vtec,rtact,iphd,ain1,ain2,sep=',')
      elif args[0]=='lason' : 
        if (len(args)>=2):
          lason=args[1]
      else:
        print()
        print (str(s))
      show_prompt()
    pyb.delay(10)  

こんなのを書いただけだけど、一応手が動くようにはなったので、休日のpythonの勉強としてはとてもよかった。