マシンオブジェクト
''原題:'Object of the machine'


状態:この文書の翻訳作業は完了しています

原文:NV133.pdf

翻訳者:caskaz

対象読者:オブジェクトについての初歩知識

By Jon Williams
Nuts & Volts コラム#133 2006年5月

先月にPropellerチップを紹介してから頭の中でぐるんぐるんPropellerが回ってたかい?
心配無用、君だけじゃなく皆おんなじさ。

先月、オブジェクト指向のspin言語について話したけれど、実際有利な点はそこじゃない。ちょっと試してみたら考えたこともないような素晴らしい事を始めることになって満面の笑顔になるのは間違いないさ。
みんなで変わろう、そしてPropellerマルチプロセッサのパワーを解き放そう。

僕は自分でもかなりのプログラマーだと思ってる。もちろんアセンブリも使ってるけど、あんまり好きじゃない。誰かが書いたアセンブラコードを使う時は好きじゃないという意味だけどね。それを読むのはちょっとした仕事だからだ。
Propellerユーザに良いニュースがあるよ。
今月は誰かが書いたアセンブラコードを使うのが全く苦にならない事を教えてあげるよ。

でもまずはちょっとしたおさらいをしよう。Propellerチップは8個の32ビットプロセッサ(Cog)が同時に動作するのは覚えているよね?
動作している全てのCogはI/Oに直接アクセスできる。system counter(遅延させる時に利用)に対しても同様だ。
“Hub”と呼ばれるシステムマネージャーがあってそれは各Cogが共有リソース(特にメインシステムRAM[32kB])にアクセスするのをコントロールしている。

CogはSPIN言語インタプリタやアセンブラ言語プログラムを実行している。
実際にはSPINンタプリタは必要な時にシステムROMからロードされるアセンブラ言語プログラムだ。
だから、アセンブラコードの容量の事で各Cogが持つ2KのRAMについて心配する必要がない。SPINコードはメインシステムRAMに置かれるのでそれは32kByteまで大丈夫だ。
(Cogに書き込まれるのはマシン語だがこれは492Longsの制限がある)

もちろん、SPINとアセンブラにはパフォーマンスで差がある。アセンブラはSPINの250倍の実行速度がある。システムクロック80MHzで設定されたPropellerチップは1秒間で8万行のSPINコードを実行できる。これはかなり速い。

さて、Propellerオブジェクトをロードしてアセンブラを使ったSPINプログラムの能力を確かめてみよう。
まず、PCのターミナルに情報を送信する”debug”を作ってみよう。これが組込み関数ではないことに驚く人もいるかもしれない、組込み関数じゃないんだよ。
Propellerは異獣だ。誰でも未使用の関数で浪費されるコードスペースの為に苦労させられるのは嫌だろう。
さて、Chipが書いたTV_terminalオブジェクトを使ってTVに値を表示させる事ができる。
実際、PC_DebugTを作るのにTV_terminalオブジェクトの数値変換ルーチンを借用したよ。

PC_Debugの目的はPCターミナルに情報を送信する事だ。その為にシリアル送信を処理するコードが必要なのでChipが書いたFullDuplex?という高機能なUARTオブジェクトを利用させてもらおう。
PC_DebugはFullDuplex?に便利なラッパーを提供してFullDuplex?のほとんどのメソッドにアクセスできるようにした。有った方がいいと思われる便利な機能も加えた。(数字を文字列に変換するなど)

今月ダウンロード用に準備したZIPファイルは特殊な名前だ。これはPropellerのアーカイブファイル名だ。
アーカイブファイルについては又あとで話すよ。
アーカイブファイルを解凍してPropellerTool?でファイルを開けたらPC_Debugを見られるよ。
このオブジェクトを使うにはOBJブロックで下記のようにするだけだ。

OBJ
	uart : “fullduplex”

プロジェクト内でuartの名前でFullDuplex?オブジェクトを使える。
uartは別のCogでシリアル通信のデータをバッファに保存してくれる。
(これはメインプログラムを実行中のCogは影響されないということだ)
これはつまり通信用のcoprocessorを持ってるみたいな・・・
かなりクールだろう?
Parallaxの方針はオブジェクトをサポートする事、stand aloneにしない事、インスタンス化(実体化)して使うstartメソッドを持つ事だ。

大抵startメソッドはコードの処理に基いてTrur(-1)かFalse(0)の値をリターンする。
これは厳格なルールではない、現状ではその方が便利だからだ。
PC_Debugはオブジェクトをサポートするようにも作っていて、下記の様にstartメソッドもある。

PUB start(baud) : okay
	okay := uart.start(31,30, baud)

これは簡単なメソッドだがまだなにも起こっていない。PUB宣言でスタートし、このメソッドをもっと高いレベルのオブジェクトによってもアクセスできるようにPUBLICにする必要がある。このメソッドはbaudと呼ばれるパラメータを必要だ。パラメータのサイズについては考慮しなくてよく、いつもLong変数だ。
又、リターンする値はコロンのあとに続く変数名(okay)に代入されている。
リターン値もLong変数だが必要ならWordやByteなどにダウンサイズできる。

今はコードはたったの1行だ:okayがuart.startメソッドのリターン値にセットされている。Spinのオブジェクトはオブジェクト指向言語で見られるドットを使っている。
uart.startメソッドは3個のパラメータを指定できる。:(受信ピン番号、送信ピン番号、ボーレート)
ここですることはPropeller standard pinを使ってuartオブジェクトをスタートさせることだ。
しかし、PCに余ってるポートがあるのか、未使用の2本のI/Oピンを使ってそれにデータを送信するのか?
問題はない。別のメソッドを作るだけだ。

PUB startx(rx_pin, tx_pin, baud) : okay
	okay := uart.start(rx_pin, tx_pin, baud)

staretx(x付き)メソッドはボーレートと使用するピン番号を単に通過させてるだけだ。
このメソッドのように同時に1つ以上ののターミナルをオープンさせることができる。(もちろん、PC側は違うポートを使用する)
Spinでは下記のようにオブジェクトの配列を定義することもできる。

OBJ
	terminal[2] : “ “pc_debug”
' Now we just need to asign the terminal to ddifferent Propeller I/O pins
PUB main
	terminal[0] : start(9600)
	terminal[1] : startx(1, 0, 57600)

上記でterminal[0]は9600baudでdefaultのピン(31,30)を使用している。
terminal[1]は 57600baudでrx_pin(1), tx_pin(0)を使用している。
Terminalオブジェクトの下層は1個のCogを使うFullDuplex? UARTである事を覚えておいた方がいい。だから上記の定義では2個のCogが動作している。

PC_Debugオブジェクトに戻ろう。これは送信データをターミナルに便利な形式で表示できるようにFullDuplex?をラッパーしている。FullDuplex?オブジェクトは新しいCogで起動しているので自身のCogを停止、又他のプロセスの為に自身を有効にするメソッドを持っている。このメソッドはstopメソッドをコールし、簡単にそれへのアクセスを提供する。

PUB stop
	uart.stop

上記は冗長なようにみえる、でも実際は違う。PC_Debugを使うどんなプログラム(top object)もFullDuplex?のメソッドに直接アクセスすることはできない。それはラッパーで提供されているからだ。この良い点は必要なものだけラッパーで提供でき、多少保護されたメソッドはそのままにしておける。
Figure133.1で完成したプロジェクトの階層を示している。PC_Debug_TestはFullDuplex?に直接接続はできない。

PC_Debugオブジェクトを見ることでFullDuplex?のオブジェクト用に他のラッパーがあるということがわかるだろう。
それらは自明なのでの詳細を述べる必要はないと思う。
プロジェクトの目的であるカスタムメソッドの説明をしよう。
大抵10進数を使うのでターミナルに表示する為に数値を文字列に変換するメソッドを見てみよう

PUB dec(value) | div, zpad
	if (value < 0)
		-value
		out(“-”)
	div := 1_000_000_000
	zpad~
	repeat 10
		if (value => div)
			out(value / div + “0”)
			value //= div
			zpad~~
		elseif zpad or (div == 1)
			out(“0”)
		div /= 10

オーケー、これは初見だと少し秘密っぽく見えるけどSpinではよく使われているということを言語の効率が好きな君に信じてほしい。
先月話したようにSpinは他の言語、いくつかのオペレータはCから借用している。

いくつか新しい事があるのでそのそこから始めよう。
1個の引数のあとに垂直線と2つのシンボル(div zpad)がある。このシンボルはローカル変数でメソッド内で使われる。ローカル変数はこのメソッドから出たら破棄されることに注意。

コードの最初は単純だ:引数が負なら引数を正数にして”-”キャラクタと数値を出力しているだけだ。次に除数(div)を初期化してzpad flagをクリアする。
いくつかcoolなことがある、32bitなので大きな数字(-2,147,483,648〜+2,147,483,647)を処理できる.
そしてSpinでは普通コンマを使う箇所に”_”キャラクタを使ってはっきりと判るようにしている。次に新しい演算子post clear(~)がある

	zpad~

これは下記と同じだ

	zpad := 0

さて、今度はdecメソッドの意味を考えよう。
このメソッドで最大値は10桁なので変換ループを10回繰り返す。
もう一度簡単なrepeat 10文に注目してくれ。これはBASICのfor x=1 to 10に置き換えられる(繰り返しは開始と最終の値を指定できるけれど)
リピートループの制御変数について不思議に感じるかもしれない。これはインタプリタのスタックからきている。

値が除数より大きいか等しいか検査して、もしそうなら除数で値を割ることによって現在の商を得てASCIIキャラクタ”0”を加算し変換する。
除数で割った余りを被除数として取り出す。数字出力の為に演算中の数字にゼロを表示するようにzpad flagを”1”にセットする。
演算子post set(~~)を覚えておいてね。
この演算子は変数の全部のビットを”1”にセットする(値を”-1”[ffffffff]にする。これはTrueとして使われる)

値が除数より小さければzpad flagまたは除数がゼロでないかをチェックする。
どちらかがtrueならゼロを表示する。最後のステップは除数を10で割ることだ。

オーケー、今度はbinary-hex変換メソッドを見てみよう。このルーチンはキチンとして綺麗だ(僕には作れない)、その上Spin言語の技巧を感じさせてくれる。

PUB bin(value, digits)
	digits := 1 #> digits <# 32
	value <<= 32 &#8211; digits
	repeat digits
		out((value <-= 1) & %1 + “0”)

このメソッドは digitsの桁数を必要としている点がdecとは少し違っている。しかし、コード自体は少ない。
演算子#>と<#を使ったdigitsについて考えてみよう。この部分はメソッドに渡される引数のチェックを行っている。
それからビット31にMSBがくるように左ビットシフトしている。他の演算子のように左シフト(<<)と(:=)は結合して一つの演算子にできる。

実際の処理はここだ:表示するdigitsの数だけループしている。まず1ビット左にローテートしている。ローテートはビットがなくなるシフトと違ってビットを回転している。
だから1ビット左ローテート(<-)するとbit31はbit0となる。
%1とANDしてそれをASCIIに変換して表示している。
僕はこのルーチンはナイスだと思う。

オーケー、16進変換の準備はできたかい?2進とおんなじだよ。
でも4ビットずつ扱うのでコードは少し増えるけどね。

PUB hex(value, digits)
	digits := 1 #> digits <# 8
	value <<= (8 &#8211; digits) << 2
	repeat digits
		out(lookupz((value <-= 4) & %1111 : “0”..”9”, “A”..”F””))

16進の1桁は4ビットで構成されるので必要桁数digitsに応じた桁数分だけ4ビットシフトの実行をする。digitsを評価した8から減算したあと、2ビットシフトする。
これは4を掛けるより効果的な方法だ。Repeat loopはbinメソッドと似た動作をする。
最後に正しいdigitsキャラクタを表示するためにlookupz(ゼロインデックス付きのlookup)を使っている。lookupzの便利な特徴はポイントの始点・終点で必要なリストを渡せることだ。“0”..”9”は”01234567789”に置き換える。
コンマでアイテムと複合リストを分けて混合リストを生成する。

PC_Debugオブジェクトが表示出力する時間のことを考えよう。アーカイブの中にPC_Debug_Test.spinがある。それはとても短く、そして
プログラム本体は既に述べたとおりだ。CON,OBJセクションはとても簡単だ。

PUB main |  idx												
	 debug.start(460_800)										
	debug.str(string(FF, “Debug Test”, CR, LF, LF))					
	  repeat													
		debug.hex(idx, 2)										
		debug.out(Space)										
		if (( ++idx // 16) == 0)									
			debug.crlf										
		until (idx == $100)									
			debug.crlf										
			debug.dec(-1)									
			debug.crlf										
			debug.ibun(-1, 32)									
			debug.crlf										
			debug.ihex(-1, 8)									
			ebug.stop										

プログラムには一つだけpublicメソッドがあり、それはmainと呼ばれる。
始めのpublicメソッドはSpinプログラムが起動した時最初に動作する。
最初にする事はdebugオブジェクトをスタートさせることだ。ボーレートを見ると460,800 – typoではない、それは460.8kBaudだ。
ChipのFullDuplex?オブジェクトは「ハイパフォーマンス」と言ったのを覚えているかい?
僕が話したことがわかっただろう。
5MHzのクリスタルを接続したPropellerチップの実力だ。

最初の行はフォームフィードキャラクタ(HyperTerminal?では画面をクリアする)、テキスト、キャリッジリターン、2行のラインフィードから出来た文字列だ。
これら全部が文字列を作りそのポインタ(メモリのアドレス)を返すstringメソッドで組み立てられている。文字列ポインタは出力の為にdebug.strメソッドで使われる。
これは一度だけなら充分だ。でも、一度以上同じテキストを使うなら下記のようにDATブロックに組んだ方がいい。

DAT
titl	byte	“Nuts & Volts rocks!”, 0										 

ゼロターミネータに注意!
これは重要だから忘れてはいけない。この文字列を出力するには変数に@演算子をつける。

	debug.str(@title)											

プログラム本体は$00から$FFまでの16進数を縦16横16で表示するループだ。
数字とスペースを表示したあと、idx値をインクリメントして現在表示している行の文字数が16かどうか剰余数でテストしている。もし16文字目ならCR、LFが挿入される。
Idx値はループの最後で終わりかどうかテストしている。
もちろん、同じ事をするのに色んな方法がある。このループの最初で下記のように組むこともありだ。

	repeat idx from $00 TO $FF									

終了処理も下記のように置き換えられる。

	If (idx == $100)											
		quit												

僕はrepeatは自由度が大変大きくてオプションも多様性があることを見せたかったんだ。

オーケー、これでPCの通信プログラムにデータ送信できるツールを手にいれたわけだ。
Spin言語のプログラミングで充分な経験をしたから外部ハードウェアを接続して始める前にそのツールを使える。

Propeller アーカイブ

今月のZIPファイルは特殊な名前だと気付いていると思う。このZIPファイルはPropeller Tool > File menuのArchiveで作られたものだ。これはIDEの非常に便利な特徴で、使用しているファイルがどこにあるかに関係なくプロジェクトの全てのファイルを集めて保存してくれる。
IDEを含むアーカイブの特徴でもある
アーカイブフォルダを数年先に開けてもプロジェクトを再構成するのに必要なファイルが手に入るよ。

Propellerで楽しんでください、次回まで・・楽しくブン回そう!
もちろん又すぐにBASIC StampとSXのプログラミングもするよ。


トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2011-12-22 (木) 11:05:11 (2431d)