SHマイコンの SFR:システムレジスタ も時間がかかるのでしょうか? 前回記事では、割込禁止フラグをアクセスするにはC/C++言語上では一行であっても、アセンブラ(実行コード)では専用のSTR命令を経由し、直接変更できないためステート数を食うことが分かりました。
デバッグ時に、E1エミューレータ等で確認できないタイミングを検証したい時 (もしくはエミューレータ等が使えず表示部がない機器 ) では、余っているマイコンポートに出力し、オシロで計測します。とっても気軽に。SHの場合、どのように実行コードが展開されるのでしょうか?
コンパイラはC/C++ compiler package for SuperH RISC engine family V.9.04 Release 03 (無償版)です。
よくやるデバッグポートやLED反転の場合、 C/C++ だと、
// ポートA.0bitを反転 Addr 0xFF464000
GPIO.PAR.BIT.PA00 = ~GPIO.PAR.BIT.PA00;
最適化ありでのアセンブラ展開は、
※ SR:ステータスレジスタ R番号;汎用レジスタ
ADDR 命令 オベランド [実行clock数,待ちclock数]
----------------------------------
MOVI20S #H'F464000,R11 ; R11 = ポートAレジスタ:PRAアドレス [1,1]
MOV #H'FE,R13 ; R13 ビット0を0にするマスク値 [1,1]
MOV.W @R11,R0 ; R0 = *R11 (PRAの状態読出し) [1,2]
AND #H'01,R0 ; R0 &= 0x01 (ビット0を立てる) [1,1]
NOT R0,R0 ; R0 = ~R0 (ビット0を0にするマスク値0xFEを求める) [1,1]
TST #H'01,R0 ; if ( R0 & 0x01 == 0 ) SR.Tビット=1 [1,1]
BF/S @H'140A:8 ; if (SR.Tビット== 0 ) goto 140A + 8 [2,2]
; (PRA00==1なら140Aの次の行に飛ぶ)
MOV.W @R11,R2 ; R2 = *R11 (PRAの状態読出し) [1,2]
BRA @H'140C:12 ; goto 140C + 12 (次行を実行し140Cの次行に飛ぶ) [2,2]
AND R13,R2 ; R2 &= R13 (ビット0を下す) [1,1]
140A BSET #0,R2 ; R2 &= 0x01 (ビット0を立てる) [3,2]
140C MOV.W R2,@R11 ; *R11 = R2 (PRAの状態設定) [1,2]
実行コードの展開量が多いですね。SFRをダイレクトに読書きしないのは変わらず。ビット反転なのになぜ分岐が必要なのか? PARを何故2回読むのか? 不思議です。R13 にマスク値を入れるのは、オベランドに固定値を直接指定できないのでしょうかね。
12ステートで総17clock、分岐があるとパイプラインの先読みがムダになるのでざっくり+10=27clock。大体 0.3us という計算です。 RL78や78Kシリーズなら以下のように一行で済むはず。
NOT1 p0.0 ' しかしbit操作系はclockやや数多め
SHでアセンブラで書くなら以下でいいような気がしますが、、、
MOVI20S #H'FF464000,R14 ; R14 = ポートAレジスタ:PRAアドレス
MOV #H'FE,R13 ' R13 ビット0を0にするマスク値
MOV.W @R11,R12 ; R12 = *R11 (PRAの状態読出し)
BLDNOT #0, R12 ; R12のBit0 を反転 → Tビット
BST #0, R12 ; Tビット → R12のBit0
AND R13, R12 ; 0xFEでマスク
MOV.W R12,@R14 ; *R11 = R12 (PRAの状態設定)
BIT操作命令はやや特殊、Bit指定は0~7まで。8以上は第2オペランドをアドレス指定でoffsetを要すようで汎用レジスタだけでは組めないようです。SHコンパイラ、SH CPUはなんか特殊そうですね。旧三菱系のM16C、R8Cもちょっと変でしたけど、、、 やっぱり最後にのこったNEC系に部があるのでしょうか…