Propeller日記 (2)
Propeller日記(1)
2008年3月10日(月)
Propellerマニュアルのチュートリアルを終えたところで、次の練習として、 Parallax社のサイトにあったPDFから、まず これを、次に これをテキストとすることにした。 ただし、このテキスト(Labと呼ぶらしい)をそのままを追い掛けるのも面白くないので、自分なりの目標を設定した。まず第一に、既にPropellerマニュアルのチュートリアルでも複数のCogsを起動する方法まで試したが、 ここでは「どこまで単独Cogで性能が出るのか」も試してみる方針とした。 クロックはPropeller Demo Boardの5MHzクリスタルでも、16倍PLLモードによって最大性能の80MHzにできるので、 そこで単独Cogでどこまでメインメモリ(強制的に時分割されて待たされる)を使って仕事が出来るのか、を試してみたい。
そして第二に、せっかくビデオ出力のサンプルを試したので、このタスクだけは例外として並列処理させて、 内部情報はいちいちビデオ表示してみることとした。 おそらく、これはテキストからは逸脱した実験となるが、今後できれば裏モニタとして、 あるいはスタンドアロンのビジュアル・インスタレーションとしてビデオ出力を活用したいので、 なるべく慣れておくためである。
最初の「Fundamentals: Propeller I/O and Timing Basics」では、 大部分のCPUが行う「入力の読み込み」「判断」「出力の制御」を整理する。 入力と出力については、たいていの場合にはタイミングに敏感であり、「入力の監視」「出力の更新」という動作となる。 このFundamentalsでは、入力の監視のサンプルとして「プッシュボタン」回路と、 出力の更新のサンプルとして「LED点灯」回路とを、PropellerチップのI/Oピンに設定して使用する。 これは応用として他の回路になっても、Propellerのソフトウェアの動作原理としては、基本的に同等である。 このLabテキストをマスターすることで、以下のような応用が出来る、という。
- Turn an LED on - assigning I/O pin direction and output state
- Turn groups of LEDs on - group I/O assignments
- Signal a pushbutton state with an LED - monitoring an input, and setting an output accordingly
- Signal a group of pushbutton states with LEDs - parallel I/O, monitoring a group of inputs and writing to a group of outputs
- Synchronized LED on/off signals - event timing based on a register that counts clock ticks
- Configure the Propeller chip’s system clock - choosing a clock source and configuring the Propeller chip’s Phase-Locked Loop (PLL) frequency multiplier
- Display on/off patterns - Introduction to more Spin operators commonly used on I/O registers
- Display binary counts - introductions to several types of operators and conditional looping code block execution
- Shift a light display - conditional code block execution and shift operations
- Shift a light display with pushbutton-controlled refresh rate - global and local variables and more conditional code block execution
- Timekeeping application with binary LED display of seconds - Introduction to synchronized event timing that can function independently of other tasks in a given cog.
Labテキストでは、「Parts List and Schematic」として、以下のような回路図が示されている。
しかし、手元のPropeller Demo Boardでは、
となっている。そこで、PDFとは決別して、Propeller Demo Boardに対応させて、 以下のようにポートの割り当てを変更することにした。 LEDについては、抵抗を経由して、本来の点灯処理と異なる見え方がある可能性を留意すればよい。
- P0-P7がフリーで空いている→ユニバーサル基板に接続可
- P8-P11はマイク入力のA/Dとヘッドホン出力アンプ用出力
- P12-P15はRCAビデオ出力用のD/A
- P16-P23は、LEDが接続されるとともに、VGA出力のための抵抗が相互を連結
実際にPropeller Demo Board上に、3つのスイッチと抵抗を配線してみた。 これ以上の回路は、外にブレッドボードを延長しないと入りそうもない。(^_^;)
Labテキストでは、これに続いて「Propeller Nomenclature」(Propeller用語体系)として、 以下のように、Propellerマニュアルで学んできた用語を整理している。 Propellerのプログラミングにはspinとアセンブラの2つの言語があるが、 Labテキストではspinを扱う、としている。 また、このLabではTop Objectを単独で扱うサンプルのみ、としている。
- Cog - a processor inside the Propeller chip. The Propeller chip has eight cogs, making it possible to perform lots of tasks in parallel. The Propeller is like a super-microcontroller with eight high speed 32-bit microcontrollers inside. Each internal microcontroller (cog) has access to the Propeller chip’s I/O pins and 32 KB of global RAM. Each cog also has its own 2 KB of ram that can either run a Spin code interpreter or an assembly language program.
- Spin and assembly languages - The Spin language is the high-level programming language created by Parallax for the Propeller chip. Cogs executing Spin code do so by loading a Spin interpreter from the Propeller chip’s ROM. This interpreter fetches and executes Spin command codes that get stored in the Propeller chip’s Global Ram.
- Method - block of executable Spin commands that has a name, access rule, and can optionally receive and return parameter values and create local (temporary) variables.
- Global and local variables - Global variables are available to all the methods in a given object, and they reserve variable space as long as an application is running. Local variables are defined in a method, can only be used within that method, and only exist while that method executes commands. When it’s done, the memory these local variables used becomes available to for other methods and their local variables. Local and global variables are defined with different syntax.
- Object - an application building block comprised of all the code in a given .spin file. Some Propeller applications use just one object but most use several. Objects have a variety of uses, depending partially on how they are written and partially on how they get configured and used by other objects. Some objects serve as top level objects, which provide the starting point where the first command in a given application gets executed. Other objects are written to provide a library of useful methods for top level or other objects to use.
さて、ここからいよいよサンプルプログラムであるが、ポートの番号を変更していること、 ビデオ出力も同時に実験することから、PDFのサンプルとは違ったソースとなる。 例えば、最初に載っていたサンプルソースは以下である。
'' File: LedOnP4.spin PUB LedOn ' Method declaration dira[4] := 1 ' Set P4 to output outa[4] := 1 ' Set P4 high repeat ' Endless loop prevents program from endingしかしこれを、実際には以下のようなプログラムに変更してみた。
{{ exp001.spin }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 OBJ Num : "Numbers" TV : "TV_Terminal" PUB Main Monitor LedOn PUB Monitor | Temp Num.Init 'Initialize Numbers TV.Start(12) 'Start TV Terminal Temp := 900 * 45 + 401 'Evaluate expression TV.Str(string("900 * 45 + 401 = ")) 'then display it and TV.Str(Num.ToStr(Temp, Num#DDEC)) 'its result in decimal TV.Out(13) TV.Str(string("Counting by fives:")) 'Now count by fives repeat Temp from 5 to 30 step 5 TV.Str(Num.ToStr(Temp, Num#DEC)) if Temp < 30 TV.Out(",") PUB LedOn dira[16] := 1 ' Set P4 to output outa[16] := 1 ' Set P4 high repeat ' Endless loop prevents program from endingこれをコンパイル、実行してみると、ビデオモニタに2行のメッセージが表示され、 さらにP16のLEDが点灯した。 ビデオ出力のMonitorメソッドは、内部的にCogに表示処理の無限ループを指定している筈だが、 呼び出し元のメソッドについては終了して戻ってくることが判った。 ここで、試しにMainの部分だけを変更して
PUB Main LedOn Monitorと反対にしてみたところ、P16のLEDは点灯したものの、ビデオには何も表示されなかった。 これは、Mainメソッドの1行目のLedOnが呼ばれて、その最後の無限REPEATから帰ってこないためだろう。
再びexp001.spinに戻して、今度はLedOnメソッドの最後のREPEATを消してみたところ、P16のLEDは点灯しなかった。 これは、メソッドが最後のステートメントまで実行されて「終了」となったため、 目に見えない一瞬のLED点灯の後に、Propellerがローパワーモードに移り、 I/Oピンが初期(待機)状態の「入力」に戻ったためである。 I/OポートにCPUが書き込むというよりは、Propellerでは、Cogは出力状態として出力レジスタを管理している。 Cogの制御を離れてもI/Oピンの状態がラッチされているのではない、というのは、 AKI-H8など他のCPUとは考え方が違うところなので、注意が必要のようだ。
Labテキストではこれに続いて、どのようにPropellerが動作しているか、 という解説が続いているが、これは既にPropellerマニュアルのチュートリアルで分かっているので、 軽くスルーした。 ただ、「複数のCogが同じ入出力ピンを使おうとした場合にはWired-ORとなる」という説明には、 ちょっと確認の実験をしたくなった。 そこで、以前に作っているOutput.spinを呼び出す、新しいexp002.spinを以下のように作ってみた。
{{ exp002.spin }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 MAXLEDS = 6 'Number of LED objects to use OBJ Num : "Numbers" TV : "TV_Terminal" LED[6] : "Output" PUB Main Monitor dira[16..23]~~ 'Set pins to outputs LED[NextObject].Start(16, 50, 0) LED[NextObject].Start(16, 1000, 0) PUB Monitor | Temp Num.Init 'Initialize Numbers TV.Start(12) 'Start TV Terminal Temp := 900 * 45 + 401 'Evaluate expression TV.Str(string("900 * 45 + 401 = ")) 'then display it and TV.Str(Num.ToStr(Temp, Num#DDEC)) 'its result in decimal TV.Out(13) TV.Str(string("Counting by fives:")) 'Now count by fives repeat Temp from 5 to 30 step 5 TV.Str(Num.ToStr(Temp, Num#DEC)) if Temp < 30 TV.Out(",") PUB NextObject : Index repeat repeat Index from 0 to MAXLEDS-1 if not LED[Index].Active quit while Index == MAXLEDSこの実験のポイントは、相次いで異なるCogでLED点灯処理を呼び出しているのに、 敢えてピン指定を「同じP16」としているところである。 コンパイルして実行してみると、P16のLEDが、
というのを交互に繰り返している。 これはまさに、P16への出力がWired-ORされている、という事である。 このexp002.spinの例では、Mainメソッドとしては、この4行のステートメントは終了している。 しかし、それぞれの中で呼び出されたCogは無限ループを走っているために、 ビデオモニタの文字もLEDの点滅動作も連続している。 プログラムの方としては、「現在いくつのCogが走っているのか」を気にしなくていい、 と言えばそうだが、ちょっと落ち着かない気分でもある。 これはまた追って、実験をしてみることにしよう。
- 1秒間、連続して点灯
- 1秒間、細かく点滅
Labテキストではこの後に、
dira[16] := 1 outa[16] := 1というステートメントを、
dira[16] := outa[16] := 1と書くこともできる・・・などと続けているが、これは混乱の元で好きではないので、パス。 これに続いて、I/Oピンの定義をまとめて出来る、というところでは、以下の例があった。
dira[16..21] := %111111 outa[16..21] := %101010この「%」は、2進バイナリでの記述方法として、多くの局面で活用するシンタックスである。これは収穫。 なお、
outa[16..21] := %101010と記述すれば、Pin16がON(1)でPin17がOFF(0)・・・となるが、これを逆にして
outa[21..16] := %101010と記述すれば、Pin21がON(1)でPin20がOFF(0)・・・となる。 つまり昇順にも降順にも対応していて、代入のビットマップは「登場した順」ということである。
Labテキストは、続いて「Reading an Input, Controlling an Output」となった。 いよいよ、初めてのポート入力である。 入力を示すのはinaレジスタであり、これはdiraでポートが出力に指定されている場合には、 そのoutaレジスタの値を返すという。 Propellerは3.3V電源で動作するので、入力ピンの電圧がスレショルド電圧の1.65Vを越えていればHigh(1)、 越えていなければLow(0)、となる。 inaレジスタの内容は、INAコマンドが実行された時に更新される。 そこで、以下のexp003.spinを作って実行してみると、 見事にPropeller Demo Boardの上のブレッドボードに置いたボタンスイッチに対応して、 Pin18、Pin19、Pin20のLEDがそれぞれ、独立にON/OFF点灯動作した。
{{ exp003.spin }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 MAXLEDS = 6 'Number of LED objects to use OBJ Num : "Numbers" TV : "TV_Terminal" LED[6] : "Output" PUB Main Monitor dira[16..23]~~ 'Set pins to outputs LED[NextObject].Start(16, 50, 0) LED[NextObject].Start(16, 1000, 0) dira[0..2] ~ ' Set to Input (redundant) repeat outa[18] := ina[0] outa[19] := ina[1] outa[20] := ina[2] PUB Monitor | Temp Num.Init 'Initialize Numbers TV.Start(12) 'Start TV Terminal Temp := 900 * 45 + 401 'Evaluate expression TV.Str(string("900 * 45 + 401 = ")) 'then display it and TV.Str(Num.ToStr(Temp, Num#DDEC)) 'its result in decimal TV.Out(13) TV.Str(string("Counting by fives:")) 'Now count by fives repeat Temp from 5 to 30 step 5 TV.Str(Num.ToStr(Temp, Num#DEC)) if Temp < 30 TV.Out(",") PUB NextObject : Index repeat repeat Index from 0 to MAXLEDS-1 if not LED[Index].Active quit while Index == MAXLEDSLabテキストによれば、このスイッチ入力とLED出力のところのステートメントは、 「outa[18..20] := ina[0..2]」というようにも記述できるという。 また、インデントが重要というのが、以下の図とともに示されていた。 これは既にPropellerマニュアルで確認済みではあるものの、今後とも注意が必要だ。
この後、システムクロックや水晶振動子の精度の話、PLLによるクロック逓倍などの記述があるが、これも既知なのでパス。 レジスタ演算については敢えて再掲しておけば、例えば
PUB BlinkLeds dira[16..23] := %111111 repeat outa[16..23] := %111111 waitcnt(clkfreq/2 + cnt) outa[16..23] := %000000 waitcnt(clkfreq/2 + cnt)というのは、ポストセット演算子「~~」とポストクリア演算子「~」を使えば
PUB BlinkLeds dira[16..23]~~ repeat outa[16..23]~~ waitcnt(clkfreq/2 + cnt) outa[16..23]~ waitcnt(clkfreq/2 + cnt)というのと同じで、これはさらにビット単位での反転演算子「!」を使えば
PUB BlinkLeds dira[16..23]~~ outa[16..23]~~ repeat !outa[16..23] waitcnt(clkfreq/2 + cnt)と同じになる、というようなこと、さらに2進数と10進数についての解説などが続いた(これもパス)。 この後には、LEDの並びを2進数バイナリと見立てて、 「outa[16..23]++」は「outa[16..23] := outa[16..23] + 1」と同じだ、 という変数インクリメントの例などが延々と続いているが、ちょっと寄り道をしてみることにした。 以下のプログラムexp004.spinでは、ビデオ出力のメソッドを再びMainとして、 ここで「スイッチを押す」or「スイッチを離す」というイベントが起きるたびに、 ローカル変数dummyをインクリメントして、その数値をビデオ出力する、というシステムにしてみた。
{{ exp004.spin }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 MAXLEDS = 6 'Number of LED objects to use OBJ Num : "Numbers" TV : "TV_Terminal" LED[6] : "Output" PUB Main | Temp, dummy Num.Init 'Initialize Numbers TV.Start(12) 'Start TV Terminal Temp := 1000 TV.Str(string("start ")) TV.Str(Num.ToStr(Temp, Num#DEC)) 'its result in decimal TV.Out(",") dira[16..23]~~ 'Set pins to outputs LED[NextObject].Start(16, 50, 0) LED[NextObject].Start(16, 1000, 0) dira[0..2]~ ' Set to Input (redundant) dummy := ina[0] + ina[1] + ina[2] repeat outa[18] := ina[0] outa[19] := ina[1] outa[20] := ina[2] if dummy <> ina[0] + ina[1] + ina[2] dummy := ina[0] + ina[1] + ina[2] Temp++ TV.Str(Num.ToStr(Temp, Num#DEC)) TV.Out(",") PUB NextObject : Index repeat repeat Index from 0 to MAXLEDS-1 if not LED[Index].Active quit while Index == MAXLEDSビデオ出力と同時に、別タスクとしてP16のLED点滅出力を行い、 さらにスイッチ入力に対応したLED点灯も行っている。 AKI-H8でもなんでも、ここまでのマルチタスクシステムは、なかなか簡単には実現できない。 これが問題なく動いたことで、ようやく、Propellerが身近になってきた。
2008年3月11日(火)
Labテキストでは続いて、Conditional Repeat Commandsのトピックである。 Propellerのspin言語には、以下のように、ループ制御の関係の多くの予約語があるようである。Syntax options for repeat make it possible to specify the number of times a block of commands is repeated. They can also be repeated until or while one or more conditions exist, or even to sweep a variable value from a start value to a finish value with an optional step delta.これと、プリ演算子とポスト演算子との組み合わせで、「20までカウントするループ」を11種類も解説する、 とあったが、いちいち試すほどソソラレないのでパスすることにした。 さらにfromとtoを使って、「20までカウントするループ」を3種類、加えて解説する、というのもパスした。 こんなのは実際に動くソフトを開発する時にちょっと実験すれば判ることだろう。 Labテキストではさらに、
と続いたが、これもPropellerマニュアルのリファレンスにある事なのて、パスしておいた。 さらに続いて、
- Propellerの演算子のボキャブラリ
- シフト演算子
- ifとelseなど
- VARブロックの変数 : byte、word、long
- Limit Minimum "#>" Operator
- Limit Maximum "<#" Operator
- 比較演算子と条件
の説明があった。 これはたまたま、昨日の最後の実験プログラムで試して、複数のローカル変数はコンマで並べられる、 と発見していたので、これもパス。
- global変数(VARブロックで定義、全オブジェクトのメソッドで共有)
- ローカル変数(メソッド名にパイプ記号「|」で並べる)
最後のTimekeeping Applicationsというトピックだけは、ちょっと重要なので注目した。 クリスタル振動子によって、システムはクウォーツ精度の正確さを持つ、というのは簡単ではない、 という話題である。 ここでは以下の2つのサンプルプログラムが提示されている。 ポート4-9に、outa[9..4]によって、secondsという定数をバイナリ表示している、 いわば「1秒時計」である。まずは「悪い時間管理の例」。
CON _xinfreq = 5_000_000 _clkmode = xtal1 + pll1x VAR long seconds PUB BadTimeCount dira[9..4]~~ repeat waitcnt(clkfreq + cnt) seconds ++ outa[9..4] := secondsそして「良い時間管理の例」。
CON _xinfreq = 5_000_000 _clkmode = xtal1 + pll1x VAR long seconds, dT, T PUB GoodTimeCount dira[9..4]~~ dT := clkfreq T := cnt repeat T += dT waitcnt(T) seconds ++ outa[9..4] := seconds「悪い時間管理の例」では、REPEATループの中のwaitcnt命令中で加算処理を実行するため、 この演算中はcntがカウントされず、全体の時間計測に遅延が加算されていく。 これが積み重なった場合に、時計としては致命的な誤差を生じる。
「良い時間管理の例」の方では、変数として定義したdTとTとでwaitcntの処理時間を計算しているので、 誤差が生まれない。 これを使った「時計」プログラムの例は、以下である。 この考え方は、オーディオのサンプリング周期とか、シリアル通信のボーレートなど、 正確な時間間隔を必要とする場面での定番となるだろう。
CON _xinfreq = 5_000_000 _clkmode = xtal1 + pll1x VAR long seconds, minutes, hours, days, dT, T PUB GoodClock dira[9..4]~~ dT := clkfreq T := cnt repeat T += dT waitcnt(T) seconds++ if seconds // 60 == 0 minutes++ if minutes == 60 minutes := 0 if seconds // 3600 == 0 hours++ if hours == 24 hours := 0 if seconds // 86400 == 0 days++ outa[9..4] := secondsこれで、 これはオシマイである。 次に、 これをやってみることにした。 タイトルはずばりMethods and Cogs。 Propellerの根幹の部分とオブジェクト指向プログラミング、 という「肝」の部分であり、まだ現時点で完全には理解できていないかもしれない「壁」である。
Introductionでは、spinにおけるメソッドについて、まず以下のように整理している。
- オブジェクトはメソッドと呼ばれるブロックから構成されている
- spinではメソッド名はプログラム制御に使われ、メソッド同志でパラメータをやりとり出来る
- あるメソッドが他のメソッドを利用するのをmethod callと呼ぶ
- callされたメソッドは終了すると、リターン値とともに呼び出し元に戻る
- メソッドが呼び出し元からパラメータ(引き数)を受け取るように記述できる
- 引き数は初期設定、動作定義、演算値などの引渡しに使われることが多い
ここからはPropeller独自のこととなるが、メソッドはそれぞれ別個のCogを並列的に起動する。 spin言語は、メソッドをCogに起動する命令、Cogを特定する命令、Cogを停止する命令を持っている。 spinメソッドがCogに起動されるためには、global変数アレイが、 メソッドのリターンアドレスをメモリ中に割り当てるために定義されている必要がある。 このメモリをstack spaceと呼ぶ。
メソッドの呼び出しとリターン、引き数の引渡しについては、CやJavaと同様である。 例えば以下の例では、メソッドBlinkTestから呼び出されたメソッドBlinkへの引き数は、 全てローカル変数のパラメータリスト項目への代入となる。 このようにすることで、異なるパラメータを用いて、このメソッドを他にも再利用して呼び出すことができる。
PUB BlinkTest Blink(4, clkfreq/3, 9) PUB Blink(pin, rate, reps) dira[pin]~~ outa[pin]~ repeat reps * 2 waitcnt(rate/2 + cnt) !outa[pin]これに対して、以下のようにstackを確保(1メソッドあたりおよそ10Longs)すると、 それぞれのCOGNEWコマンドに対応したCogが起動して、並列処理が実現できる。 それぞれのCogsを起動するTopレベルのメソッドには自動的にCog(0)が使われるので、 起動される3つのCogsは、新たにCog(1)からCog(3)となる。 メソッドBlinkは共通に呼ばれるが、それぞれ引き数パラメータは異なり、 点滅の時間は異なった処理を実現できる。
この例では、Cog(0)は3つのステートメントで3つのCogsを起動すると、 もうステートメントが無いので、ここでシャットダウンする。 他の3つのCogsもそれぞれのREPEATループを終了すると、 リターンを監視するCogも無いのでシャットダウンする。 最後のCogの処理が終わると、PropellerはLow Power Modeになる。 ただし、組み込みシステムを作る場合には、この状態にはならないで、 無限ループが続く筈である。
Cog(0)が未使用のglobal RAMをアクセスして格納してくれるまでの間、spinメソッドを実行する他のCogsは、 自分のメソッドcallのリターンアドレス、ローカル変数、中間演算表現を持っていなければならない。 このためにglobal RAMに確保するテンポラリ記憶領域をstack spaceと呼び、stackとして定義する。 上の例ではlong stack[30]と記述することで、30要素のLong配列を確保した。 Cog(0)が自分自身のstackとして使用するのは、Propeller Toolのメモリ一覧でブルーの領域、 未使用RAMの先頭部分である。 stack領域が端数が飛び出している場合には、4Longs(16bytes)単位で次の先頭の部分である。
ここまでに登場していたCOGNEWコマンド(引き数はメソッドとstackの2つ)は、たとえば
cognew(Blink(4, clkfreq/3, 9), @stack[0]) という場合、Propeller自身が自動的に、 次に空いているCogを割り当てるので、実際にどのCogであるかは実行するまでわからなかった。 ところが、COGINITコマンド(引き数はCog指定とメソッドとstackの3つ)というのがあり、
coginit(6, Blink(4, clkfreq/3, 9), @stack[0]) とすると、これは明示的にCog(6)を実行させることができる。 プログラミングとしては、こちらの方がスッキリするようにも思う。 ただしリファレンスマニュアルによれば、COGINITは返り値が無いとのことなので、 実行した場合、既に何か走っていたプロセスは強制終了されるらしい。
CogをストップさせるのがCOGSTOPコマンド(引き数はCog指定の1つ)であり、 使い方としては
cogstop(Cog) のようになる。 これは明示的にCogの指定が必要なので、これまでに扱った例では、 COGNEWして「空いているCogのID」を取得すると、COGNEWの返り値であるCog IDをglobal変数に格納し、 それを使ってSTOPしていた。 しかし、全てのプロセスのCog IDを明示的に固定するというのも、それほど悪くないように思う。
テキストではこれに続いて、ここまで謎だったstackの詳細を解説している。 stackが呼ばれると、それぞれ以下の領域が必要となるという。 これまでどうも歯切れが悪かった理由は、固定長でなく可変長であるからのようだ。
- 2 - return address
- 1 - return result
- number of method parameters
- number of local variables
- workspace for intermediate expression calculations
一例として、「Blink(pin, rate, reps)」が呼ばれる場合のstackであれば、
- 2 - return address
- 1 - return parameter
- 3 - pin, freq, and reps parameters
- 1 - time local variable
- 3 - workspace for calculations.
となって、10Longsのstackが必要となる。 これは正確には、以下のルールによるのだという。 面倒なので、とりあえず10Longsとか12Longsでいいかなぁ。(^_^;)