x86リアルモードでモニターを作ろうシリーズです。
Windows11+wsl2で構築しています。
前回は 1桁除算(A ÷ digit) まで実装しました。
今回はその続きで、多桁 ÷ 多桁 に進みます。
ただし、最初に言っておきます。
今回の除算は「商を推定しません」。
なぜ推定しないのか
多桁除算というと、
- 上位桁から商を推定
- 掛けて引いて
- 外れたら修正
という方法がよく紹介されます。
でも、今回扱っているのは 10進・1桁=0..9 の BCD です。
つまり、1桁の商は 最大でも 9。
ならば、
引けるだけ引いて、その回数を数えればいい
という、極めて単純な方法が使えます。
- 推定が不要
- 外れも存在しない
- 最大でも 9 回で確定
リアルモードで事故らせないなら、こちらの方が安全です。
基本方針(再確認)
今回の多桁除算は、以下の前提で実装しています。
- 計算はすべて 絶対値
- 符号は外側で処理
- DHS(unpacked BCD)で演算
- 商は 上位桁から1桁ずつ確定
やっていることは、完全に小学校の筆算です。
除算の流れ(全体像)
- 余り R を 0 で初期化
- 被除数の最上位桁から順に
- R = R × 10
- R = R + a[i]
- R >= 除数 B の間
- R -= B
- 商の桁を +1
- 商の1桁が確定
- 次の桁へ
この 2~4 を桁数分繰り返します。
実装の核心部分
; R = R * 10
mov si, bx
call dhs_mul10
; R += a[i]
mov al, [si + DHS_VAL + cx]
call dhs_add_digit
; q_digit = 0
mov byte [tmp_div_qdigit], 0
mov byte [tmp_div_guard], 10 ; 0..9 を超えない安全装置
.try_sub:
dec byte [tmp_div_guard]
jz .store_qdigit
; if R < B then break
mov bp, [dhs_div_b_local]
mov si, bx
call dhs_cmp_abs
jc .store_qdigit
; R -= B
mov bp, [dhs_div_b_local]
mov si, bx
mov di, dhs_div_tmp
call dhs_sub_abs
mov si, dhs_div_tmp
mov di, bx
call dhs_copy
inc byte [tmp_div_qdigit]
jmp .try_sub
このループは、
- 比較
- 引き算
- 回数を数える
だけ。
tmp_div_guard について
mov byte [tmp_div_guard], 10
これは「保険」です。
- 10進1桁なので、最大でも 9 回
- バグって無限ループになるのを防ぐ
実装中に自分を救ってくれるタイプのコードです。
この方法の良いところ
- 実装が単純
- フラグとレジスタの破壊が追いやすい
- normalize のタイミングが明確
- デバッグしやすい
速度は速くありませんが、
リアルモードでは 正しく終わることの方が重要 です。
実際に動かしてみると
- 小さい数でも
- 桁数が多くても
- 同じ流れで処理できる
という、非常に素直な挙動になります。
「計算している感」がちゃんとあります。
ここまで来て思うこと
add / sub / mul / div が揃ったことで、
ようやく 10進整数を「普通に扱える」状態 になりました。
64bit に収まらない数を
無理に 2 進で扱わなくていい、というのは気が楽です。
おわりに
これで10進20桁の演算ができるようになりました。40桁にしようと思えば簡単にできます。それにしても……
いったいわたしは何を目指しているのでしょう???
自分でもわからなくなってきました。
今回はここまでです。
読んでいただきありがとうございました。
このソースはgithubで公開しています。よろしければどうぞ。
https://github.com/CbWB-Inc/software/tree/main/laboratory/lab02/hyfax-07-digit
《2026/1/13 16:01:21》