円周率の計算 (7)
STmicroelectronics 社の開発環境 ST Visual Develop (STVD) 用の spigot アルゴリズムのアセンブラ・ソースプログラムを下に示します。
STM8S Discovery 上で実行すると、PD5/UART2_TX 端子 (コネクタ CN4 の 10 番ピン) から 38.4 kbps シリアルでπの値を小数点以下 40 桁出力します。 値が正しいのは 37 桁までです。
3.1415926535 8979323846 2643383279 5028841666
STM8S Discovery の基板上には RS232C インターフェース回路は搭載されていないので、PC 等と接続するには外部に回路を設ける必要があります。
シリアル出力と同時に、内部の RAM の 0x0100 番地以降にも ASCII 文字列として値を格納しますから、デバッガでメモリ・ダンプすれば結果を見ることができます。
クロックの設定は変更していないので、デフォルトの 16 MHz 内部高速クロック (HSI) を 8 分周した、HSI / 8 = 2 [MHz] の CPU クロックでの実行になります。
また、STVD のデバッグ・ターゲットをシミュレータに設定すれば、STM8S Discovery がなくても、ソフトウェアだけで実行させることができます。
アセンブル方法としては、まず、STVD で新規ワークスペース/新規プロジェクトを作成します。
そして、下のプログラム・リストをコピー・アンド・ベーストした「spigot.asm」ファイルをプロジェクトのフォルダの中に作成します。
spigot.asm
stm8/ ;*************************************************** ;* spigot.asm : pi calculation by spigot algorithm * ;* using 128 byte array x 1 * ;* 37 decimal digit accuracy * ;* * ;* 2011-08-20 : Created by pcm1723 * ;*************************************************** ; ; UART2 registers ; UART2_BASE equ $5240 UART2_SR equ {UART2_BASE+$0} UART2_DR equ {UART2_BASE+$1} UART2_BRR1 equ {UART2_BASE+$2} UART2_BRR2 equ {UART2_BASE+$3} UART2_CR1 equ {UART2_BASE+$4} UART2_CR2 equ {UART2_BASE+$5} ; ; constant equates ; f_cpu.l equ 2000000 ; cpu clock = 2 MHz (HSI/8) bps.l equ 38400 ; 38.4 kbps uart_div.w equ {{f_cpu+{bps/2}}/bps} brr1_val.b equ {{$0ff0 and uart_div}shr 4} brr2_val.b equ {{$0f and uart_div}or {{$f000 and uart_div}shr 12}} outdigits.b equ 40 ; output decimal digits nterms.b equ 128 ; number of terms nsteps.b equ 4 ; base.b equ 100 ; base 100 number n_lim.b equ {nterms-{2 mult outdigits}} ; ; global variables in page 0 ; segment 'ram0' num.b ds.b nterms ; array for pi calc temp.b ds.w 1 ; (word) temporay obufidx.b ds.w 1 ; (word) output buffer index n.b ds.b 1 ; (byte) loop counter n out.b ds.b 1 ; (byte) output value ; ; global variables ; segment 'ram1' obuf ds.b 256 ; output buffer segment 'rom' ; ; Spigot main ; .spigot ; implicit public label callr setup_uart ; setup uart callr out_crlf ; output CR/LF ld a,#nterms ld n,a ; n <-- nterms ; array initialize dec a ; A <-- (nterms-1) exg a,xl ; XL <-- (nterms-1) ld a,#2 for_i_loop_0 ld (x),a ; num[i] <-- 2 decw x ; --i jrne for_i_loop_0 ; branch if not ld out,a ; out <-- 2 ; ; now, X == 0 for_n_loop ; ; X as (temp), Y as (i) ; 'for(i' setup ld a,n ; A <-- n dec a ; A <-- (n-1) exg a,yl ; YL <-- (n-1) for_i_loop ld a,yl ; A <-- i mul x,a ; X = temp * i ldw temp,x ; save current temp ld a,(y) ; A <-- num[i] ldw x,#base ; X <-- base mul x,a ; X = num[i] * base addw x,temp ; X = temp * i ; + num[i] * base ; calc denom in A = 2 * i - 1 ld a,yl ; A <-- i sll a ; A <<= 1 dec a ; A -= 1 div x,a ; X = temp / denom, ; A = temp % denom ld (y),a ; num[i] <-- temp % denom ; 'for (i' iteration decw y ; i -= 1 cpw y,#1 jrne for_i_loop ; branch if (1 != i) ; output result digits ld a,#base ; A <-- base div x,a ; X = temp / base ; A = temp % base exg a,xl ; A <-- quo., XL <-- rem add a,out ; A <-- out + temp/base callr out2dig ; output 2 digits ld a,xl ; A <-- rem. ld out,a ; out <-- temp % base ; 'for (n' iteration ld a,n ; A <-- n sub a,#nsteps ; A -= nsteps ld n,a ; update n cp a,#n_lim ; compare to limit jrnc for_n_loop ; branch if not end ret ; ; UART setup and init output buffer index ; setup_uart mov UART2_BRR2,#brr2_val ; b15..b12,b3..b0 mov UART2_BRR1,#brr1_val ; b11..b4 mov UART2_CR1,#0 ; enable UART, 8N mov UART2_CR2,#%00001100 ; Rx, Tx enable clrw y clrw x ldw obufidx,x ; clear buffer index ret ; ; output CR/LF ; out_crlf push a ; save A ld a,#$0d ; CR callr outc ld a,#$0a ; LF callr outc pop a ; restore A ret ; ; output 2 digits ; out2dig push a ; save A pushw y ; save Y pushw x ; save X clrw y ld yl,a ; YL <-- out ld a,#10 ; div y,a ; Y = out / 10 ; A = out % 10 exg a,yl ; A <-- (out/10) ; YL <-- (out%10) ldw x,obufidx ; output buffer index jreq out2d1 ; skip the very first '0' callr outnum ; output 1st digit out2d1 exg a,yl ; A <-- YL (out%10) callr outnum ; output 2nd digit ; ld a,#11 ; 10 digit + 1 delim pushw x ; save buffer index div x,a ; A = idx % 11 popw x ; restore buffer index cp a,#1 ; 1 == (idx mod 11) ? jrne out2d3 ; no delimiter needed ld a,#'.' ; decimal point cpw x,#1 ; test for integer digit jreq out2d2 ; branch if so ld a,#' ' ; blank out2d2 callr outchar ; output delimiter out2d3 ldw obufidx,x ; update buffer index popw x ; restore X popw y ; restore Y pop a ; restore A ret ; ; output a char to UART ; outnum add a,#'0' ; cnvert to ASCII outchar ld (obuf,x),a ; store char in buffer incw x outc btjf UART2_SR,#7,* ; wait until Tx empty ld UART2_DR,a ; output char via UART outc_ret ret end
そして、STVD 上で「spigot.asm」を「プロジェクトに追加」し、新規プロジェクト作成時に自動生成された「main.asm」に下のように 2 行追加します。
main.asm
stm8/ #include "mapping.inc" extern spigot.w ; ******* この行を追加 ******* segment 'rom' main.l ....... < 中略 > ....... clear_stack.l clr (X) incw X cpw X,#stack_end jrule clear_stack call spigot ; ******* この行を追加 ******* infinite_loop.l jra infinite_loop ....... < 後略 > .......
以上の準備でアセンブルができます。
アセンブル結果の S フォーマット・ファイルを下に示します。
S00600004844521B S1138080AE07FF94AE00007F5CA300FF23F9AE01AE S1138090007F5CA305FF23F9AE06007F5CA307FF06 S10B80A023F9CD80A820FE8025 S11380A8AD48AD5CA680B7844A41A602F75A26FCBF S11380B8B785B6844A61909F42BF8090F6AE00644B S11380C84272BB0080909F484A6290F7905A90A3EE S11380D8000126E2A6646241BB85AD2F9FB785B631 S11380E884A004B784A13024C981350452433503DC S11380F8524235005244350C5245905F5FBF82812D S113810888A60DAD3DA60AAD3984818890898990E9 S11381185F9097A60A906261BE822702AD1E61AD88 S11381281BA60B896285A101260BA62EA300012795 S113813802A620AD09BF828590858481AB30D70122 S10E8148005C720F5240FBC7524181E3 S113800082008080820080A7820080A7820080A7EF S1138010820080A7820080A7820080A7820080A7B8 S1138020820080A7820080A7820080A7820080A7A8 S1138030820080A7820080A7820080A7820080A798 S1138040820080A7820080A7820080A7820080A788 S1138050820080A7820080A7820080A7820080A778 S1138060820080A7820080A7820080A7820080A768 S1138070820080A7820080A7820080A7820080A758 S9030000FC
ソースからアセンブルしなくても、このファイルを ST Visual Programmer (STVP) で STM8S Discovery に書き込んでも実行できます。