CA78K0コンパイラで、16bit × 16bit = 32bit演算が狂う事象が発生しました。コード概略は、
U16 a=125; U16 b=430; U32 c; c = a * b; // c=10になる
WindowsやAndroidアプリでは考えられない事象ですが、OS無しの16bitマイコンありがちな事象です。しかし、乗算結果の上位16bitが捨てられるありえそうですが、意味不明な演算結果です。試しにキャストを明示してもNGでした。
c = (U32) ( a * b ); // これもNG
仕方がないので、アセンブラのコード展開をみてみました。
movw ax,[hl+4] movw _@RTARG0,ax movw ax,[hl+8] call !@@iumul clrw bc movw [hl],ax xchw ax,bc movw [hl+2],ax
乗除算は、マイコンライブラリにまとめられているようです。16bit × 16bit = 32bitのmulhu命令が、なぜダイレクトに展開されないのでしょう? マイコンライブラリはあまり使わないようにしますが、元々使われていたら仕方ありません。でも怪しいのは、「clrw bc」のところです。明らかに16bit捨てられています。
キャストを明示した場合では、axレジスタの方がクリアされます。
movw ax,[hl+4] movw _@RTARG0,ax movw ax,[hl+8] call !@@iumul movw _@RTARG0,ax clrw ax movw _@RTARG2,ax movw [hl+2],ax movw ax,_@RTARG0 movw [hl],ax
マイコンライブラリを使用しないようにすると解消する気もしますが、全体的に、ロードモジュールに影響が生じます。正しい結果が得られるC言語の書き方を、TRY&ERRで探すしかいりません。
今回は、以下の書き方で直りました。
U16 a=125; U16 b=430; U32 c; c = a; // 一旦32bit変数に入れる c *= b; // c=53750になる
アセンブラ展開コードは、、、
movw ax,[hl+4] clrw bc xchw ax,bc movw [hl+2],ax movw ax,[hl+8] movw _@RTARG4,ax movw _@RTARG6,#00H movw ax,[hl] movw _@RTARG0,ax movw ax,[hl+2] movw _@RTARG2,ax movw ax,_@RTARG6 call !@@lumul movw ax,_@RTARG2 movw [hl+2],ax movw ax,_@RTARG0 movw [hl],ax
clrw命令がなくなり、上下16bitがスタックに格納されていることが確認できました。だいぶコードが増えたのが気がかりです。