/var/log/hdk.log

2020 年 1 月中旬


10 (金)

%1 きんようび

晴天。

高田馬場駅の地下鉄と JR 線の乗り換えって、けっこう遠いんだな。 初めて知った。

%2 Portfolio

きのうの 37 AAA 命令、足した場合が 8 クロックで足さない場合が 9 クロックか? 先日の LEA 命令の推定を使って検証しよう。

2400 は and $0,%al で、AF をクリアするとともに %al の下位 4 ビットを 9 以下にする。040f は add $0xf,%al で AF をセットするか %al の下位 4 ビットを 9 より大きくする。 やっぱり、2400 のほうが 1 クロック長いように見える。 合っている気がする。 ただし LEA 命令 7 つ並べた場合についてはちょっと違うかも。 たぶんその場合は AAA 命令の実行を始めた時点で一時的にキューがいっぱいになって、その後の取り込みが遅れていると思われる。 もう少し検証できることがありそう。

2020/01/10 のコメントを読む・書く


11 (土)

%1 どようび

のんびり寝た日。

%2 DAA 命令

AAA 命令が 8086/8088 でなんで 8 クロック (9 クロック?) なんだろうと考えていたら、むしろ DAA 命令が 4 クロックなのなんでだろうという方向に進んでしまった。 そして DAA 命令のオペレーションをインテルのマニュアルで確認していたら、新旧での違いを見つけた。 というか、新しいほう、書き直しをミスったみたいで、無意味な行が残っている。 無意味な行を消して新しいほうを雑に JavaScript 風に書くとこんな感じだ:

function daa(old_AL,old_CF,old_AF){
 var AL=old_AL,CF,AF;
 if((AL&0xf)>9||old_AF){AL+=6;AF=1;}else{AF=0;}
 if((old_AL&0xff)>0x99||old_CF){AL+=0x60;CF=1;}else{CF=0;}
 return [AL,CF,AF];
}

で、古いやつ... 2001 年の日本語版のソフトウェア・デベロッパーズ・マニュアルのを書くと、以下のような感じ:

function daa(AL,CF,AF){
 if((AL&0xf)>9||AF){if((AL&0xff)>0xf9)CF=1;AL+=6;AF=1;}else{AF=0;}
 if((AL&0xf0)>0x90||CF){AL+=0x60;CF=1;}else{CF=0;}
 return [AL,CF,AF];
}

さて、両者は同じなのか? 考えてみた。 自分は頭がよくないので、なんとなく同じような気がするがうまく説明できない。 そこで、上のプログラムを変形させていくのがよさそうだ。 新しいものを古いほうに近づけていく。 まず、old_* がいらなくなるように一度 if 文を展開する。

function daa(AL,CF,AF){
 if((AL&0xf)>9||AF){
  if((AL&0xff)>0x99||CF){
   AL+=6;AF=1;
   AL+=0x60;CF=1;
  }else{
   AL+=6;AF=1;
   CF=0;
  }
 }else{
  AF=0;
  if((AL&0xff)>0x99||CF){AL+=0x60;CF=1;}else{CF=0;}
 }
 return [AL,CF,AF];
}

で、if((AL&0xf)>9||AF) の {} の中を整理していく。

 //AL+=6;AF=1;を外に出す
  AL+=6;AF=1;
  if(((AL-6)&0xff)>0x99||CF){AL+=0x60;CF=1;}else{CF=0;}
 //AL-6の-6を右辺に出す。桁あふれに注意する。
  AL+=6;AF=1;
  if((AL&0xff)>0x9f||(AL&0xff)<6||CF){AL+=0x60;CF=1;}else{CF=0;}
 //(AL&0xff)>0x9fは(AL&0xf0)>0x90と等しい
 //CFを先に書き換えることで(AL&0xff)<6をなくす
  if((AL&0xff)>0xf9)CF=1;AL+=6;AF=1;
  if((AL&0xf0)>0x90||CF){AL+=0x60;CF=1;}else{CF=0;}

これで半分は同じになった。 あとは if((AL&0xf)>9||AF) の else のほうだが、こっちは (AL&0xf)<=9 であることが確定しているため、(AL&0xff)>0x99 は (AL&0xff)>0x9f と読み替えることができる。 すると上と同じように (AL&0xf0)>0x90 に読み替えられるので、CF を扱う部分が同じなり、それを外に出してまとめれば、同じ内容を導くことができた。

おそらくこれは本当にプロセッサ内で行われているオペレーションが変更されたんだと思う。 まぁ誰も DAA 命令なんて今時使っていないだろうから放置でもよかったんじゃないかと思うんだけど、まじめにマニュアルを書き換えて、ミスって変な CF の代入文が残っちゃったんだろうな。

ちなみに、直接関係はないけど、AAA 命令のオペレーションは、古いマニュアルでは AL と AH を別々に足すようになっている。 これは 8086/8088 は確かにそういう動作をしていたのだが、80286 以降は AX に対して足し算するようになった、ということが「80x86/80x87 ファミリー・テクニカルハンドブック」に書かれている。 まぁ、この本もしれっと間違ったことが書かれているところもあるので (SETALC 命令についてもこの本は 80286 以降としていたが実際はもっと前からあった)、80286 以降が正しいのかどうかはわからないけど、少なくとも 1993 年出版のこの本より遙かに新しい 2001 年のソフトウェア・デベロッパーズ・マニュアルには、新しいオペレーションが載っているべきだったとは思う。

で、DAA 命令が 4 クロックで済む謎は未解決。AAA 命令が長いのは、AL の論理積を取るためか、あるいは、AH の変更を伴うためか、だと思う。 そのへんは、undefined になるフラグや、例の JMP FAR AL で ALU の手前のテンポラリーレジスターの値を取り出せば推測できるかも? DAA 命令は undefined になるフラグは OF しかないし、実際 4 クロックでできることは限られていると思う。9 との比較でなく、6 を足して AF を見ているのかなとも考えたが、そうすると DAS は足した後引き算しなきゃいけないのでさらに面倒になる。 もしかしたら 8080 あるいはもしかしたら 4004 の名残の回路が作ってあったのかも知れないな。

DAA 命令で複数桁の 10 進数の足し算を処理する場合、下の 2 桁から順番に進めて、ADC 命令で CF を含む足し算をしつつ DAA 命令で補正していけばいいようになっている。 次の 2 桁に進む時点で AF はどうでもいい。1 桁ずつの場合は ADC 命令と AAA 命令で進めて、同じように AF は無視して CF だけ見ればよく、AH に 1 を足されるのも無視することになると思う。

%3 4004

go4004/docs at master - asicerik/go4004 - GitHub

読んで楽しいドキュメント。4004 (MCS-4) にも DAA 命令はあったらしい。4004 のアキュームレーターは 4 ビットだから今とは意味が違うけど、でも 9 を超えているかキャリーがセットされていたら 6 を足して、それでキャリーはクリアしないよというのは、まさに x86 の古いマニュアルのほうに載っていた挙動に通ずるものがある。

引き算については、4004 は引き算のキャリーの意味が反対だったみたいだし、足し算の補正 DAA はあっても引き算用はなくて、代わりにキャリーを見てアキュームレーターを 10 か 9 かにする TCS というのがあったらしい。

ちょっと感心したのはインデックスレジスターのインクリメント命令がキャリーを壊さない点。8086/8088 になってもその仕様は汎用レジスターのインクリメント命令に受け継がれているが、アキュームレーターが 4 ビットしかなかった 4004 では、いわゆる多倍長演算はほとんど必須みたいなもので、隣のアドレスに進めながら演算を進めるのに、インデックスレジスターのインクリメント命令がキャリーを壊さないのが大事だったんだ。8086/8088 はアドレッシングが豊富になって、インクリメント命令を使わなくともアドレスを進めることができるようになっていたが、それでも CF を壊さない仕様は残したんだな。 後になってこの仕様が高速化に影響するとは思いもしなかっただろうな。

2020/01/11 のコメントを読む・書く


12 (日)

%1 にちようび

レンタルカート。 藤野。6 回券で 4 回 (うち 2 回は 10 分走行) 乗って、ベストタイムは 39.35 秒あたり。

3 連休中日、しかも天気が怪しい、ということで、その午前中にさっと行ってさっと乗って、昼には帰る方向。 朝の気温の低さから安全を考えて車で、八王子から向こう側だけ高速使用。 狙い通り、道はめちゃくちゃ空いていたし、雨が降る前に帰り着けた。

なんだかんだ言ってやっぱり山間部は高速のほうが時間短縮度合いが大きいな。 国立府中 IC から八王子 IC までは一般道でも 30 分弱程度か? 今日は特に午前中は空いていたこともあって、結構流れが良かった。 全部一般道で行くと、八王子市内に入るまではそう遠い感じでもないけど、高尾のあたりまでたどり着くのにだいぶ時間が掛かって、大垂水峠越えでさらに 30 分近く掛かるイメージだもんな。

道中で見かけた教習車。 トヨタドライビングスクール、未だにコンフォート教習車を使っているのか。 そういえばトヨタの教習車、今はカローラベースか? 見かけたコンフォートは MT 仕様で、比較的加速は元気がいいのでガソリン仕様かな? 信号待ちでミラーに映った運転手をよく見たら、40〜50 代ぐらいの雰囲気の人だった。 仮免許練習中でも、MT に手こずっている様子はなく、制限速度まで一気に加速するし、進路変更も危なげない感じで、これは免許取り直しの人かな。 そうだとすると横に乗っている教官も暇だろうな。(完全に想像だけど。)

%2 電卓

4004 の説明を斜め読みした後、電卓のソフトウェアを実装してみたくなった。 ここは 4004 用のプログラムを書いた人達の気持ちを想像しつつ、8086/8088 向けの実装を作ってみるか。DOS 用でアセンブリ言語で書けばいいだろう。 演算だけでも、10 進数ベースで小数に対応した四則演算に平方根まで実装するのは地味に大変。 おまけに、実際の電卓の UI って独特な操作法あったよな? とよけいなことを考え始める。123*456= で答えだした後に、0+= と押すと元の数 123 が出るみたいなのがあったんだよな。 あれどういう仕様だっけ...

10 進数での足し算引き算は、4004 でもあったように補正命令を使えばそう難しいものではない。 かけ算は、各桁同士のかけ算をして足していくことになると思うが、8086/8088 では MUL 命令と AAM 命令を使えということだろうか? そうするととても実行時間が長くなるので、実用性が怪しくなりそうだが...

4004 あるいは 8080 など、かけ算命令がない CPU ではどうしていたのだろうか? 電卓となるとテーブル参照するほどメモリーも余裕ないだろうし、やはり足し算か。 うまくやるなら 2 のべき乗単位で処理するんだろうとは思うけど、それはそれでレジスター足りるのかな。 まぁ最悪 9 回足し算してもそう時間はかからないだろうけどね。

割り算は割り算で計算量がやばいし、平方根はそれらの組み合わせで、4004 で実用的な速度で動かすのって相当難しいのでは... みたいな気分になる。 あと負の数をどう表現するか、2 の補数ならぬ 10 の補数という手はあるけど、表示が手間だからやっぱり符号は別管理かな。

2020/01/12 のコメントを読む・書く


13 (月)

%1 成人の日

ハッピーマンデー (死語)。

電卓と電池を買いに調布のビックカメラに行くつもりが、バイク駐車場が満車。 周辺にあった調布市のバイク (or 原付) 駐車場がことごとく廃止されてしまったため、せっかくできたショッピングセンターのバイク駐車場は明らかに不足してしまっている。 ほんの 30 分でいいから無料で駐められるところがあれば、使うのにねぇ。 で、どうするかって? 三鷹市に行くわけですよ... こんなで調布市は本当にいいんだろうか?

結局コジマ×ビックカメラで電卓も電池も買った。 ビックカメラのポイントカードも使えるし、そんなに店は広すぎず、そんなに混雑もしていなくて、こっちのほうが便利なこともありそう。 ビックカメラの取り置きサービスがこっちでも使えたらいいのにね。

今日もバイクを動かしてみたが、キーの接触不良は起きなかった。 まぁいいか。

車がだいぶ汚いことに気づき、適当に軽く洗車した。 排水のためにやや傾斜のついている駐車場で、車を手で押して後退させるのは、まぁ、やってみるとできないこともないな。

%2 電卓

シャープの安い電卓を買ってきた。 店頭にあったものではキヤノンが一番安かったが、平方根のキーがなさそうだったので避けた。 カシオは高いので買わなかったけど、触ってみたところ相変わらず百分率キーの動作が違う。100+8% と押した時に 108 にならないやつ。108 にしたければ、100×8%+ とする。 そこまでは昔説明書で読んで覚えていたが、100+8% で出てくる数字が何の意味だったかは忘れた...

ま、いいや。 あとは適当に遊んでみる。 まず小さいほうの桁あふれはどうなるかというと、8 桁電卓なので 10+0.00000001=10 となった。 他の数字も入れてみたが、下は切り捨てのようだ。 実にシンプル。 上はあふれればエラーになる。99999999+1=1.0000000E である。 よくある電卓らしい挙動。

さて、クリアした状態で、次に数を省略して演算を実行した時の結果。 表示を {} で囲んで示す。 末尾の小数点は省略。

先に演算子キーを押した場合は素直に 0 を対象として計算されているのに対し、後で演算子キーを押した場合は +- と×÷で挙動が違うことがわかる。+- は先に押した場合と同じ挙動になっているが、×は同じ数同士のかけ算として扱われ、÷は 1 を割っている。(×= と÷= は説明書にも載っている。) ほー。 で、その後でただ = を押せば、最後の演算を繰り返す感じ... だが、ここで、数字だけを入れて = を押すという技が存在する。 以下の 4 パターンは (例の数字は違うが) すべて説明書にも載っている。

このように、かけ算だけ左側の数が残り、他は右側の数が残っている。 さて、演算子キーを押して = を押すのは、説明書には×と÷のケースしかない。 他はどうなるのか?

このように、+ でも - でも結果は同じで、元の右側の数との演算になり、しかし残る数は新たに入力した数である。 いや、この右側というのは正確ではなくて、さっきの、数字だけ入れて = を押した場合に残る数を表す。 それを確かめる。

これで、記憶にあった 0+= の挙動も説明できる。 で、数字を入れずに演算子だけを入れて = を押すのも OK で、画面に表示されている数に対して上の法則が適用される。 四則演算のすべての組み合わせでやってみるとこんな感じになる:

ところで、= の代わりに演算子のキーを押しても計算結果が求まるようになっている。 その場合に数を省略するとどうなるのか?

ほー。 これはつまり、クリアーした状態から数を入れて演算子と = を押した時と同じ状態だ。 内部の状態は飛んで、表に見えている数だけが残るのか。

さて、パソコンや携帯電話で動く電卓アプリ・機能というのは、十中八九、このような挙動にはなっていない。 大は小を兼ねると言わんばかりに、演算子の優先度まで考慮する関数電卓的な動作のものが多い。Windows の標準の電卓はそうではないのだけど、Windows XP までさかのぼっても、演算後の 0+= は 0 になる。 この電卓の通りの挙動になるようにプログラムを作れと言われたら、作れるだろうか。 プログラムでなくとも、設計書でもいい。 ひとつひとつは説明できる挙動なので、理屈の上では作れることはわかっているんだけど、結構時間が掛かる自信がある (笑)。

というわけで雑にまとめるとこうだ:

┌────┬──────┬───┬──────┬───────────┐
│ 入力 │演算子の左側│演算子│演算子の右側│見えない部分に残るもの│
├────┼──────┼───┼──────┼───────────┤
│+数= │見えていた数│ + │入力された数│演算子とその右側の数 │
│−数= │見えていた数│ − │入力された数│演算子とその右側の数 │
│×数= │入力された数│ × │見えていた数│演算子とその右側の数 │
│÷数= │見えていた数│ ÷ │入力された数│演算子とその右側の数 │
│+=  │   残   │ + │見えていた数│演算子とその右側の数 │
│−=  │   残   │ − │見えていた数│演算子とその右側の数 │
│×=  │見えていた数│ × │見えていた数│演算子とその右側の数 │
│÷=  │   1   │ ÷ │見えていた数│演算子とその右側の数 │
│=   │見えていた数│ 残 │   残   │更新されない     │
├────┼──────┴───┴──────┴───────────┤
│=後の数│見えている数を入力された数に置き換える          │
└────┴─────────────────────────────┘

こんな簡単な仕様すら知らないで電卓を使っていたなんて... かけ算と割り算が独特なのは、べき乗計算とか 1/n 計算とかに便利なようにだとは思うけど、かけ算だけ残される数が反対なのは何が要因なんだろうな。3 かける 2 の n 乗、みたいなことをやるのに 2×3===... と入れるのはやや違和感があるようにも思うが、でもこれは自分の記憶からして昔からこういう仕様だったと思う。

あとはクリアーやメモリーの挙動も興味深い。 昔は AC だったが、今はなぜか CA という表記が使われている。 メモリーのほうも、MC ではなく CM となっている。 上のように 1+2===... とやっていて、C/CE を押して = を押すと 0 になる。 しかし、その前に 0 でも何でもいいので何か押してから C/CE を押して = を押すと、0 を対象に +2 の演算が繰り返せる。C/CE を 2 度押せば 0 になるが、C/CE と 0 を交互に押していれば +2 の情報は残り続ける。MR に相当する RM キーはこの電卓にはなく、R/CM キーになっているが、これは見えている数を置き換える効果があるし、数を入れれば追加ではなく新たな数の入力が始まるが、1+2===... の時の +2 は残り続ける。

さて、ごちゃごちゃと細かい話を書いたが、最近はまっている CPU の挙動調査も、これと似たようなものである。CPU のほうが見えない部分が多くて調べるのが大変だけど、手探りで内部の実装を推測するという意味では、やっていることは大差ない。

2020/01/13 のコメントを読む・書く


14 (火)

%1 ZenFone Max Pro (M1)

ZenFone Max Pro (M1) は、ピュア Android と言われる、ほとんどメーカーのカスタマイズが入っていない Android スマートフォンだが、Android 8.1.0 でセキュリティパッチは去年の 2 月で音沙汰が無くなっていた。 まぁ所詮 ASUS、前の ZenFone Max もほとんどセキュリティパッチは出なかったのでそんなもんだろうなと思っていたのだが、きのう、どういうわけだか Android 9.0 へのアップデートが降ってきた。 それで検索してみるとどうやら先月のうちにニュースリリースがあったようである:

「ZenFone Max Pro (M1) (ZB602KL)」に、Android 9.0への FOTAアップデート開始

今更感が半端ないが更新があるのは悪い話ではないので、サクッとアップデートした。1 日使った印象は以下のような感じ:

ま、いいか。(だめだと思ったとしてももはや戻す手段はない。)

%2 電卓

きのう考えたあの動きをどう実装できるのかを考えていたけど、やはり状態管理が必要か。 整理していくと状態はどうやら 4 つだな。S0〜S3 としよう。 数字の保存場所は表裏の 2 つあって画面には表が表示されており、フリップできるとする。 全クリア後は裏は 0 を保持していて演算子は + (あるいは表示機能がある場合は + に相当する演算子無し) で S0 からスタートと思えばいいかな。

×÷特別処理はきのう書いたアレで、×は同じ数同士のかけ算、÷は 1/n を計算する。

演算実行は今の表を演算子の左に、裏を右に置いて演算をする。 わかりにくいが引き算で考えるとはっきりする。6-2= を計算させると裏に 2 が残るので、そのまま 8= と打てば当然 6 が出る。 ここで、7-= と打つと... = のタイミングでフリップし表裏が入れ替わる、すなわち、表に 2, 裏に 7 が来る。 この状態で表を演算子の左に置いて演算をするので、-5 が出る。

やや長くなっている部分もあるが、状態としてはこれ以上は削れないと思う。 えっ、メモリーはどうなるかって? 試してみたんだけど、M+ と M- は、押した時点で = で確定して S3 に移り、その時の表がメモリーに転送されて、裏は 0 保持で演算子もリセット状態っぽいんだな。CA, ÷2, M+ などといじわるなことをしても、その後 3= などと入れても 0 になるので、メモリーができようができまいが (メモリーは 0 になると消える)、消されている感じ、いや違う。= で確定してから M+ 押せば裏は消えないな。 だから S2 の演算子入力に近い挙動だ。 数字を入れても飛ばないので、S1・S2 からのメモリーが、裏が消える条件だ。

2020/01/14 のコメントを読む・書く


15 (水)

%1 すいようび

きのうの夜は暖房無しで寝られた。 室温 13 度ぐらいあるとまぁ、何とかなるもんだな。

未明に降っていた雨は昼前にとっくにやむ予報かと思っていたが、昼ご飯の頃はまだ降っていて、食べ終わって店を出てきたら日が差し始めていた。 そんな天気。

F1 はシーズンオフであまりニュースがないと思って読む頻度が下がっているが、フジテレビの F1 解説で有名な今宮純氏が、今年初めに亡くなっていたという、10 日のニュースを今頃知った。 あらら。 有料放送になってからフジテレビの F1 中継は見ていないため、あの声は懐かしい感じだが、F1 ニュース記事ではまだ名前を見かけていた。 虚血性心疾患とニュースに出ているので突然のことだったみたい。RIP。

2020/01/15 のコメントを読む・書く


16 (木)

%1 もくようび

なんか急に冷えてきたというか、週末の天気予報に雪マークが付いている。 その割には気温は低くない予報。 来週は最低気温が低そうだが、それでも氷点下行くか行かないかぐらい。 暖冬か。

%2 電卓

足し算引き算で符号の扱いはどうしているのだろうか、と考えてみたんだけど、単純に、人がやるのと同じように、符号を見て、符号を除いて足し算をするパターンと、符号を除いて大小比較をして、どっちかの向きで引き算をするパターンに分ければいいのか。 コンピューターちっくに 10 の補数なんて手も考えてみたけど、電卓の作りには合わない気がする。

買った電卓には +/- キーが付いている。1+ と押してから、+/- キーを押して、それから 2= とすると、1 と出る。+/- キーはその時の状態にかかわらず、画面に出ている内容の符号を反転するというわけである。

なお、もしパソコンで動く 8 桁電卓のシミュレーターを作りたいだけなら、今時なら 32bit 整数値を使って作ってしまったほうが楽だと思う。 いや、10 桁や 12 桁だとしても 64bit 整数値を使えばいい。 小数については、2 進数の浮動小数点で扱うと誤差が出てしまって、電卓らしい挙動にならないので、32bit や 64bit の整数値に 10 のべき乗 (0 以下) を掛けた数として扱うことになると思う。 その形式なら入力中に 0.000 などとなっているところも表現ができる。 符号については、入力時は -0 があり得るので別扱いがいいが、計算後は -0 はあり得ないので 2 の補数で扱ってもいいのかな。

と、まぁ、考えていると、小型化された電卓というのは日本ではそろばんを置き換えるものとして登場して普及したんだな、という気がしてきた。 そろばんは雑に言えば数の記憶装置であり、それこそ家計簿を付けるだとか、買い物をする時の合計金額を出すだとか、そういう時に計算を間違えにくいように使われていたわけである。(自分が子供の頃にはすでに電卓やレジが普及していたけど、どこかの小さな商店でそろばんが使われているのを見たことがある気がする。) 電卓も数の記憶装置だけど、計算もしてくれる。 コンピューターらしい 2 進数による計算ではなく、10 進数で計算してくれるので、まさにそろばんをはじくのと同じ結果が得られる。

その後、より多くのデータを計算する目的では、パソコンの表計算ソフトも普及したんだけど、それは 2 進数による計算が一般的なので、小数を扱う場合は注意が必要である。IT 業界ではよく知られている話だけど、例えば 10 進数の 0.1 は 2 進数では循環小数になる。 軽い気持ちで +0.1 とか +0.2 とかした数を扱うと、引き算などで簡単に誤差に遭遇してしまう。 例えば 0.1 の桁までしか使わないことがわかっているなら、あえて 10 倍した整数値を使うといった工夫が必要になることもあり得る。 電卓を使うのにもしそういう知識が必要だったとしたら、そろばんがもっと生き残っていたかもね?

2020/01/16 のコメントを読む・書く


17 (金)

%1 きんようび

職場のビルの停電案内、突入電流のおそれがあるから機器の電源は外しておいてくれというのはいつもの案内だけど、機器の例としてペンタブというのがあって、ペンタブって省略形は一般的なのかなと思いつつ、それってあのスタジオ会社以外にあるんだろうかと...

%2 Portfolio

DAA 命令。 TF テストプログラム使用。JMP FAR AL により ALU の手前のテンポラリーレジスターに残っている値を調べた。

上位 8 ビットは 0xff、8 ビットのメモリー読み取り時と同様の挙動で、下位 8 ビットには足した数が残っているっぽい。 ...えっ? DAA 命令って 2 回に分けて足すんじゃなかったの? そうか。 結果は同じだから、ALU の使用回数を減らすために、まとめて足していたのか。 ということは、どちらかと言えば現行の Intel SDM に載っているオペレーションに近いのでは? 9 との比較で AF と CF を必要に応じてセットして、AF と CF に基づいて足す数を作って、加算実行、みたいな。 そしてたぶん、9 との比較は ALU 使わないでできるようにしてあって、それで 4 クロックで済んでいるのかも。0x99 との比較は下の 9 との比較結果と上が 9 と等しいか大きいか、そのへんの組み合わせだろうな。 この前みたいに JavaScript 風に書けばこんな感じ:

function daa(AL,CF,AF){
 if((AL&0xf)>9)AF=1;
 if((AL&0xff)>0x99)CF=1;
 AL+=(CF?0x60:0)+(AF?6:0);
 return [AL,CF,AF];
}

この様子で行くと DAS 命令も同じノリだろうから、DAS 命令は一部の組み合わせだけ試そう。

やはり同じだな。 フラグは OF が未定義だが、見た感じ普通に演算結果が反映されているかな。DAA 命令のほうもオーバーフローになる値を試してみよう。

それっぽい。AAA 命令については先月少し試したが、その前半の処理、AL に対して足し算を行う部分は DAA 命令の半分と同じだと思う。SF 何これって書いているけど、もしかして足し算の時に出た SF が残っているのかな。0 でも足しているってことかな。AND はどうやっているのかよくわからないけど、ハードウェア的に下位 4 ビットだけを取り出す仕組みを作ってあるのだろうか、あるいは、AH に 1 を足すだけに 4 クロックもは掛からず、ALU で AND までやっているのかも知れない。

しかし... クロック数のチェックでは AAA 命令はどうやら足さない場合に 1 クロック長いっぽいことが判明している。 なんでだろうな? フラグレジスターの関係だろうか? DAA 命令の感じで行くと、AF も CF もセットだけでクリアはしない。AAA 命令も AF はセットだけで良いけど、CF については AF と同じ値にセットするなりクリアするなりしなければならない。 例えば AAA 命令は DAA 命令の半分だけでいいので、CF も AF と同じようにセットするような仕掛けがあるのか? それで CF のクリアが必要だと 1 クロック追加になるのか? いや、もしかして半分じゃなくて、全部、完全に DAA 命令と同じことを一度やっているかも? どうせ 0xf でマスクしちゃうから、途中で何やっていても結果はわからないんだよね。

その AAA 命令をもうちょっと試すか。

フラグの様子を見る限り、未定義の OF, SF, ZF, PF はすべて、AND を取る前の AL の足し算の結果と見てよさそう。 そして、0x9f を超える値を AL にセットしていても、SF がセットされている、つまり、0x66 を足した結果にはなっていないので、DAA 命令と全部同じことをやってはいないらしいことが把握できた。

しかしそうすると、やっぱり 1 クロックは不思議だな。 素直にフラグを反映しながら 0 を足せば AF も CF もクリアできるんだから、何も難しいことはなさそうなものだけどな。 まぁそれを言ったら AH だってそもそも AL に変更がないなら足す必要もないので、マイクロコードってそこまで制御の自由度はないものなのかも。 かけ算割り算はだいぶクロック数に幅があるから自由度高そうに見えるんだけどな。AAA 命令も、AH の足し算は CF と一緒に 0 を加える ADC 命令相当のことをやっていそうな気が。

2020/01/17 のコメントを読む・書く


18 (土)

%1 どようび

寒い日。 雪が降った。

JavaScript は、計算のアルゴリズムを試すくらいなら非常に便利だなとあらためて思った。HTML に適当に JavaScript を書いて web ブラウザーで開いて確認するだけでなく、web ブラウザーに付いている Web Console 機能を使えばその場で変数の中身の確認や各関数の呼び出しもできるし、console.log() を使って途中経過を Web Console に出力させることもできる。Web Console には地味にコマンドヒストリー機能も付いているし、複数行の入力も可能だし、関数の中身を差し替えることも可能である。

こうして遊んでいると、やっぱり、昔のパソコンの BASIC にあたるものは JavaScript じゃないかと思うのである。 ややとっつきにくさがあるかも知れないが、今時のオペレーティングシステムが普通にインストールされているパソコンさえあれば、他のソフトウェアの購入やインストールが一切不要で、すぐに実行結果が確認でき、簡単な計算から、興味があるならアクションゲームみたいなものまで作れる。 問題はとっつきにくさかな、コンピューターで計算がしたいというならまだ触りやすいと思うが、インタラクティブなアプリケーションを作ろうと思うと HTML の知識が必要になるのでわかりにくいかも。 そういう人向けの情報がまとまっている本があればいいんだろう。(ないわけがないけど。)

%2 電卓

足し算引き算の場合、小数点の位置を合わせて計算することになるが、この位置あわせは最初に行われ、はみ出す桁はそこで捨てられる。 例えば 8 桁の電卓で 99+0.000001 はもちろん 99.000001 で 8 桁に収まっており何も問題はなく、99.999999 もキャリーが処理されて正しく 100 になるが、さらに足しても 100 より増えない。 足し算ではおそらく、どういうアルゴリズムで処理しても、数字 8 桁分で切り捨てるならそうなると思う。 しかし、反対に 100-0.000001 を計算しても 100 が出てくる。 同じ 8 桁処理であっても、アルゴリズムによっては以下のような流れで別の数になるのではないかと考えたが、そうではなかったということだ。

0.0000000 (0 に 1 桁ずつ答えを入れていく形でスタート)
0.0000009 (0.000001 の 1 を引く時に対応する桁がないため 0 と見なして計算)
0.0000099 (以下ボローを処理)
0.0000999
0.0009999
0.0099999
0.0999999
0.9999999
9.9999999
99.999999 (100 は百の位まであるため、ここで一番下の桁を捨ててボローを処理)
099.99999 (もうひとつ桁を捨ててボローを処理し 0 が出る) -> 99.99999

なお、桁数が多い 10 進数の演算の確認には bc -l コマンドを使用している。 これはデフォルトで小数点以下 20 桁の演算ができる便利なコマンドである。

電卓内部にはもう少し多い桁数のバッファーがあるのではないか、と想像していたがちょっと違うかも知れない。 かけ算、どうしているんだろうな。 ひとまず桁あふれを簡単に調べたところ、2.1111113×9 みたいな計算をしても 19.000001 と出ており (一番下の 7 が切り捨てられている)、下の桁から計算していそうな雰囲気はある。(上からやれば 2×9 の時点で 18 が出てしまい、一番下の桁にたどり着かないと見た。)

平方根は、ずいぶん昔に開平法のやり方を教えてもらって、BASIC でその方法で 10 進数演算のプログラムを書いたことがあるが、開平法のやり方は忘れてしまった。 しかし、ぼけっと数学的に考えていたら、うまくやれば足し算引き算だけでもできそうだなと思った。(x+1)^2-x^2=2*x+1、というシンプルな関係があるので、各桁を 1 ずつ増やして大小比較をして確定させていくのはそう難しい話ではないんだ。 足し算引き算だけにするのは置いておくとして、頭の中で考えていた方法をもとに、試しに 200000000 の平方根の整数部分 14142 を整数演算で求める簡単なプログラムを書いてみると以下のようになった:

(function(x){
    var a=0,c=1,c2=1;
    while(x>=c2)c*=10,c2*=100;
    while(c>=10){
        c/=10,c2/=100;
        var d=c2+2*c*a,dd=c2+c2;
        while(x>=d)a+=c,x-=d,d+=dd;
    }
    return a;
})(2*100000000);

これもやっていることは実は開平法と同じかも知れない。2*100000000 の 2 を 1 から 9 まで変えて試すと結果は以下のようになった:

 10000, 14142, 17320, 20000, 22360, 24494, 26457, 28284, 30000

よさそう。

2020/01/18 のコメントを読む・書く


19 (日)

%1 にちようび

そういえば、きのう・今日とセンター試験の日だったな。 たまたま通りがかった国立大の最寄り駅の前や、私立大の入口前に、センター試験関連の誘導担当っぽい人や、センター試験会場の看板を見かけた。 センター試験って今年が最後らしいけど、始まったのは 1990 年らしい。 へぇー。 なんとなく昭和から続いていたのかと思っていた。 その前は大学共通第 1 次学力試験という似たようなのがあったものの私立大は別だったらしい。

東村山のほうのハードオフまでバイクで行ってみた。 ふんふん。 それから東大和のほうに向かおうとしたら、なぜか新青梅街道の案内がなくて久米川駅のロータリーを抜ける羽目になってしまった。 なんで案内がないんだよw ひでぇなw

%2 新しい Microsoft Edge

噂の Microsoft Edge の新しい Chromium ベースのバージョンを自宅 Windows PC にインストールしてみた。 この PC では前から Microsoft Edge を主に動画再生に使っていて、それは動画再生の調子が良いのが理由。 それと、Mozilla Firefox はプライベートブラウジングの設定にしてあるので、有料動画配信サービス DAZN にログインしっぱなしにしておくのに便利というのもある。 別の格安ノート PC でも有料動画配信サービス DAZN の動画再生がなめらかという事例もあって、結構大事なポイントなのである。

で、インストールしてさっそく DAZN を開いてみると、ログインされていない。 クッキーは飛ぶのか。 なんてやつだ。 移行してくれよ。 まぁいいや。 ログインして、適当に動画を再生しようとしたら、エラー 11_012_012。 おいおい、なんだよそれ、困るよ。 アニメが増えていてタッチがあったので選んでみたけどやっぱりエラー。 何かエラーなく再生できる動画はないか、探したら、サッカーの見逃し配信が再生できた。 しかし... ちょろっと 2, 3 秒再生しては、10 秒ほどくるくる表示で止まる、のを繰り返している。 タスク マネージャーで CPU 使用率を見るとめちゃくちゃ高い。 タブはひとつしか開いていないのに Microsoft Edge がいくつもあって、しかもそれらがめちゃくちゃ CPU リソースを消費している。 えー。

動画サイト全般がだめなのかと、YouTube を開いてみたら、YouTube は問題なく再生できる。 画質を 1080p にしても問題なさそう。 つまり DAZN 側の問題はあるのかも知れないが、しかし今までの Microsoft Edge ではむしろスムーズに再生できていたのである。 まぁ何か DAZN 側が変わったのかも知れないので、新しい Microsoft Edge をアンインストールして、今までの Microsoft Edge で DAZN を開いてみると、エラー 11_012_012 も出ないし、サッカーの試合も引っかかりなく再生できた。 ウーン。

アンインストールするとなんでアンインストールしたのか理由を教えてみたいなページが開いた。 なぜか英語だったので、クソみたいな英語で DAZN 見られないから消した的なことを書いておいた。4 月になったら勝手にアップデートされちゃうんだっけ? どうすりゃいいんだよ...

特に問題がないという人もいると思うけど、たぶん低スペック PC での動画再生がやばいのではないか。 自宅 Windows PC は Athlon 5350 をクロック周波数を落として 1.1GHz で使っているし、格安ノート PC は Atom x5-Z8350 だから、試していないけどおそらくさらに厳しいやつだ。(昨今の CPU の脆弱性対策のため同程度のスペックでも Intel CPU のほうがより厳しい状況になる。) GPU の動画再生支援が働かないのか? でもそれなら YouTube が問題ないのは説明ができない。 単に Chromium の実装がタコなのでは...

そんなわけで Chromium を試してみたらどれもエラー 11_064_011 で再生できなかった。Mozilla Firefox では再生はできるんだけど、やや重そうな感じ。

%3 給油

138 円/l。 燃費計算 17.8km/l。 燃費表示 20.5km/l。

前回の給油が雑だったのでこれである程度補正される。 前回分と合わせて計算すると 19.7km/l になる。

%4 電卓

かけ算のやり方を考えていた。 きのうの 2.1111113×9 のように、下の桁からやっていそう、しかし出力が 8 桁なのでどこかで切り捨てをしなければならない、じゃあ下の桁から確定していく方法かな、などと考えていたのだけど、下の桁から確定していくのはそれはそれで面倒な上にありがたみが少ない。 一番下の桁同士を掛けたら一番下の桁が確定し、2 番目の桁と 1 番目の桁の組み合わせ (2 個) をそれぞれ掛けて足せば 2 番目の桁が確定し、というふうな計算は理論的には可能だけど、一番下の桁が確定した時に 2 番目の桁へ繰り上がるというか、2 桁目の数があってそれを残しておかなければならず、しかも被乗数と乗数を最後までとっておかなければならないので、メモリーに制約がある場合はなかなか厳しそう。

で、そういえば 4004 には乗算命令はない。1 桁同士のかけ算結果を小さな ROM に載せておくことは不可能ではないだろうが、1 桁同士のかけ算結果の 2 桁を足していくにしても、キャリーを処理しなければならないので、毎回何桁もの足し算を行う羽目になる。 それだったら... 普通に全桁の足し算をしてもよくないか? 少なくとも計算用に 8 桁のメモリーが追加で必要なのは間違いなさそうだし、一番下の桁からやるなら 0 にして桁あふれのキャリーをそこに足すようにすれば、例えば 2.1111113 を 9 回足して、9.0000017 と、桁あふれの 1 が 9 のところに代わりに入る的な感じで... (あまり深く考えてはいないので何か間違っているかも知れない。)

2020/01/19 のコメントを読む・書く


Powered by Tomsoft Diary System 1.7.4

/var/log/hdk.log コメント一覧

トップ / 日記索引 / 日記 (2020 年 1 月中旬)

Hideki EIRAKU