質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

CPU

CPUは、コンピュータの中心となる処理装置(プロセッサ)で中央処理装置とも呼ばれています。プログラム演算や数値計算、その他の演算ユニットをコントロール。スマホやPCによって内蔵されているCPUは異なりますが、処理性能が早いほど良いとされています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

5回答

3052閲覧

アセンブリプログラムの解説

carnage0216

総合スコア194

アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

CPU

CPUは、コンピュータの中心となる処理装置(プロセッサ)で中央処理装置とも呼ばれています。プログラム演算や数値計算、その他の演算ユニットをコントロール。スマホやPCによって内蔵されているCPUは異なりますが、処理性能が早いほど良いとされています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

4クリップ

投稿2018/03/02 20:16

編集2018/03/18 22:45

アセンブリ言語のプルグラムに関する質問です。
ある本でアセンブリを勉強をしているのですがわからない部分があります。
横幅が4の倍数協会にない場合のダミーデータを計算するようなのですが、
なぜダミーデータを計算するのかわかりません。
横幅が4倍でない場合にレジスタと演算回路で計算して4で割り切れない場合の余りを捨てるという意味でしょうか?というかアセンブリプログラムはそのように書いてあるのでしょうか?
個人的に4倍ではなく1バイトが8bitなので8で割ったほうが都合がよいように思えますが。

計算部分とアセンブリ言語のプログラムのある部分に疑問があります。
以上について質問します。疑問のある部分に丸で囲んだ数字を置きます。

wMargineをゼロクリアにして、widthをeaxレジスタに代入します。
4で割った余りを算出するためにeaxレジスタを3で論理積を行う。
(⓵ここで3倍した理由は4で割った際の余りが3以上になるようにするためですか?
だとしたらもともとダミーデータが出ないように計算したほうがいいように思えますが。)
最下位2bitのいずれかが立っているかを検査します。
もし、4で割ったときに余りがあったら、widthを3倍し、それを4で割って得る。そして4から余りを引いてwMargineに設定する。
(⓶「そして4から余りを引いてwMargineに設定する。」の部分の4から余りを引く理由がわかりません)

プログラム通りeaxレジスタにwidthを入れます。そしてebxレジスタに3を代入し乗算します。
(⓷eaxレジスタとebxレジスタを乗算するのはわかるのですがプログラムにはmul ebxとしか書いていないのですが、なぜmul ebxと書いただけでeaxレジスタとebxレジスタを乗算する結果がebxに入る理由がわかりません。演算結果を一旦別のレジスタに入れた後に、mov命令でebxに演算結果をいれるならば理解できます。このアセンブリプログラムを出力したCPUの構造故にmul ebxと書いただけでeaxレジスタとebxレジスタの乗算結果がebxに入るのでしょうか?だとしたら仕方ないです。)

その後4バイトの乗算であるため上位4バイトがedxと下位4バイトがeaxレジスタへ代入されるようです
(⓸この乗算結果を二つのレジスタに乗算の結果を分割して入れている命令部分はプログラムのどこに書いてあるのでしょうか?
どのようにアセンブリ命令を書くことで分割してレジスタに入れられたのでしょうか?)

最後にダミーデータを知るために下位4バイトを入れたeaxレジスタと0x00000003で乗算して余りを取得したと書いてあります。これでダミーデータが得られたそうですが、なぜ得られたのか解説をよんでもいまいちわかりません。(⓹下位4バイトを入れたeaxレジスタと0x00000003で乗算して余りを取得したと書いてあります。これでダミーデータが得られた理由がわかりません。また何で割ったのかわからないのですがアセンブリプログラムには書いてあるでしょうか?)

プログラムは以下の通りです。

mov wMargine, 0 mov eax, width /* 幅 */ and eax, 0x00000003 /* %4 */ jz no_margin xor edx, edx /* クリア */ mov eax, width /* 幅 */ mov ebx, 3 /* 3 */ mul ebx /* 幅x 3 */ and eax, 0x00000003 /* %4 */ mov ebx, 4 /* 4 */ sub ebx, eax mov wMargine, ebx

以上5つの質問でありますが、どうか解説して頂けると大変助かります。
C言語を勉強している上でより技術を磨くためにアセンブリ言語を身に着けたいと思います。
どうかよろしくお願いいたします。

修正
論理積と足し算を勘違いして使っていました。
ようやく回答者様の皆様の回答が理解できてきました。
以後できる限り気を付けて質問します。
どうかよろしくお願いいたします。
本当に申し訳ありませんでした。

3/19 復習している際に疑問が出来たので再度以下の事に関して質問したいのですが、ここでは書きにくいため新しく質問を作ります。回答していただけると大変ありがたいです。
一時的に質問をしないとのことでしたが今回に関してはどうかお許しください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

おそらくビットマップの仕様

水平方向のバイト数が4の倍数ではないときは、0x00で埋めて4の倍数にする。

です。(なんか他にもavi/jpg/png/gif/mpegの仕様で幅に制限あるフォーマットがあった気がするけど思い出せない)

個人的に4倍ではなく1バイトが8bitなので8で割ったほうが都合がよいように思えますが。

1bitには0か1の情報しか詰められませんけど、モノクロ画像でしょうか?

> ⓵ここで3倍した理由は4で割った際の余りが3以上になるようにするためですか?

24bit bmpなのでは?

> ⓶「そして4から余りを引いてwMargineに設定する。」の部分の4から余りを引く理由がわかりません

目的はwidth * 3 + wMargine== 4の倍数にしたいのです。
wMargineが1~4とした場合

n = width * 3 / 4 width * 3 + wMargine= 4 * (n + 1)

この連立方程式を解いてください

編集:計算するとおかしかったので追記
数の性質です。
ある数nの4で割った余りが

  • 1の時、n+3は4で割り切れる
  • 2の時、n+2は4で割り切れる
  • 3の時、n+1は4で割り切れる
  • 0の時、nは4で割り切れる

> ⓷eaxレジスタとebxレジスタを乗算するのはわかるのですがプログラムにはmul ebxとしか書いていない

> ⓸この乗算結果を二つのレジスタに乗算の結果を分割して入れている命令部分はプログラムのどこに書いてあるのでしょうか?

以前の回答でx86_64の仕様書を貼り付けましたので割愛
不明な命令くらい自分で調べましょう。

投稿2018/03/02 21:48

編集2018/03/02 22:12
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

carnage0216

2018/03/02 22:08

モノクロ画像です。 解答ありがとうございます。 参考にさせて頂きます。 あの余談なのですか、なぜモノクロ画像かもと推測出来たのですか? アセンブリプログラムをみて推測したのでしょうか? どうかよろしくお願いします。
asm

2018/03/02 22:24

少なくとも、このプログラムでのファイルフォーマットとしては24bppで扱ってるように見えます。 > 1バイトが8bitなので8で割ったほうが都合がよい のはモノクロ(1bpp)のみです。 なんとなく勘違いしてるんじゃないかと疑問に思ったのが、2のn乗-1との論理積(AND)は簡易的な余算であって決して乗算ではないです。
carnage0216

2018/03/02 22:51

修正、どうもありがとうございます。 あの大変申し訳無いのですが、「2のn乗-1との論理積(AND)は簡易的な余算であって決して乗算ではないです。」がうまく理解できません。 例えば、2^-1×3だとして 3/2より、余算は1となるのような事でしょうか? 勘違いしているみたいで申し訳ありません。 どうか理解したいので具体例などを挙げて教えて頂けないでしょうか? どうかよろしくお願いします。
asm

2018/03/02 23:00

3 = 0b11=2^2-1との論理積(AND)を考えます 下2bitのマスクなので 0&3=0 1&3=1 2&3=2 3&3=3 4&3=0 5&3=1 これは2の2乗=4と割り算した時の余りと等しくなります。
carnage0216

2018/03/02 23:22

わざわざどうもありがとうございます。
rubato6809

2018/03/03 23:27

carnage0216さんへ > 以前の回答でx86_64の仕様書を貼り付けましたので割愛 > 不明な命令くらい自分で調べましょう。 mul命令に関する③、④の疑問は、mul命令の解説・命令の仕様を見れば疑問でも何でもないことだと私も思うので、基本、自分で調べていただきたいことです。 そこで確認したいのですが、現時点でmul命令を調べましたか?疑問は解消しましたか? それを確認したうえで、このアセンブリコードに関してコメントしたいと思っています。
rubato6809

2018/03/04 06:26

mul命令に関する疑問は解消しましたね?
carnage0216

2018/03/04 06:48

はい、解決しました。
carnage0216

2018/03/04 07:08

asmさんに質問なのですが、ダミーデータを扱うことと画像をモノクロにすることに何か関係はあるのでしょうか? 自分からダミーデータのプログラムの質問をしておいて失礼なのですが、単純にダミーデータなどを気にしないでモノクロにできないのでしょうか? 本を読みながら勉強していたのですが、ダミーデータが出ていたとしても捨ててしまえばいいのにアセンブリプログラムではそのダミーデータを捨てえることなく複雑に扱っているかのように思えて理解しにくくなってきました。 どうかよろしくお願いします。
episteme

2018/03/04 07:41

画像の左端が4の倍数だと扱いが楽(=高速)なんですわ。
carnage0216

2018/03/04 07:47

それでわざわざ複雑にアセンブリプログラムが書いているのですね。
carnage0216

2018/03/04 07:53

あともう一つ疑問があるのですが、4で割ればあまりが出ます。この余りがダミーデータではないのですか? アセンブリプログラムではこの余りをさらに4で引くのですか? そうしないとダミーデータの長さを知ることができないのでしょうか? 単純に4で割った後に得られたダミーデータでは長さがわからないのでしょうか? やはりこれも高速化のためなのでしょうか?
rubato6809

2018/03/04 08:08

> 単純に4で割った後に得られたダミーデータでは長さがわからないのでしょうか? 今、私が回答したなかで検証しています。高速化のため、ではありません。きつい言い方をすれば、怠惰が書いたコードとも言えますね。
carnage0216

2018/03/04 08:11

では割った余りをさらに「4で引く」行為に意味はないのですね? となると4で割るだけでよかったと解釈します。
rubato6809

2018/03/04 08:16

> 4で割るだけでよかったと解釈 結論はそうですが、ご自分でトレースしてみてください。結果だけを鵜呑みにすることは違う問題で過ち犯しかねない危険があります。全てを理解することはできないし、全てを理解しようとすることは却って理解の妨げになることもありますが、それでも、できるだけ中身を理解するように努める姿勢は大事です。
carnage0216

2018/03/04 08:28

あの、どうもありがとうございます。 正直アセンブリコードの初めの部分しか理解できていませんが、どうやら無駄な部分もあるようなので全体を理解でき、自分でソースコードが書けるようにしたいと思います。そう簡単には身に着けられそうにないですが。
hmmm

2018/03/04 08:39

ビットマップの話なら求めたいのはパディングする長さですよね? であれば、asmさんの回答に記載のあるように最終的に 4 - (4で割った余り) となるのはおかしくないです。 質問には全く記載されいませんが、何の処理をしているプログラムの一部なのか記載すべきです。
carnage0216

2018/03/04 09:12

そうです。ビットマップの話です。 そうですね。失礼しました。 あの、widthを4で割ったとの余りがパディングの長さとして使えないのですか? hmmmさんのおっしゃる、最終的に 4 - (width4で割った余り) も不要だとは思いませんが、他の方の質問を見た限りいらない部分であるように、おかしくないとは思いません。しかし、必要かどうかを考えるとwidthを4で割ったとの余りがパディングの長さが使えるならば、その余りをパディングとして使ってしまえばいいので、さらに4で差し引くことはおかしくはありませんが、必要でない気がします。 もしかしたら間違った解釈故に間違ったことを言っているかもしれませんがどうかよろしくお願いいたします。 ソースコードは以下の通りです。    xor edx, edx /* クリア */ mov eax, width /* 幅 */ mov ebx, 3 /* 3 */ mul ebx /* 幅x 3 */ and eax, 0x00000003 /* %4 */ mov ebx, 4 /* 4 */ sub ebx, eax mov wMargine, ebx えーと、mov ebx, 4 /* 4 */  sub ebx, eax   の部分が余りを4で差し引く部分ですが必要ないように考えています。 しかし自分なりにない場合のソースを書いて実行していないためもしかしたら必要であると認識させられるかもしれません。
asm

2018/03/04 09:23

widthの単位がなんなのか、wMargineの単位がなんなのかを考える必要があります。
carnage0216

2018/03/04 09:27

両方ともバイト単位で考えています。
asm

2018/03/04 09:32

本にもそう書いてあるのでしょうか? 単位が同一ならば3倍する必要がないですよね? ビットマップのフォーマットについては調査されましたか?
carnage0216

2018/03/04 09:41

すいませ読み間違えていました。 本書にははっきりとは書いていないのですが、widthを3倍すると横幅のバイトを知ることができると書いてあるので、widthはbit単位と私は推測したのですが、間違いでしょうか?
carnage0216

2018/03/04 09:41

ビットマップのフォーマットについては調査していません。
carnage0216

2018/03/04 09:48

あの、mov eax widthという命令に関しては機械の方はwidthがbit単位の変数であることだと認識しているのでしょうか?というかbit単位の変数を扱う時のレジスタなどが決まっているのでしょうか? あるいは人間のほうがwidthがbit単位でも計算が成り立つように工夫しているのでしょうか? 僕は後者の方に思えますが。
asm

2018/03/04 09:53

ビットマップの仕様については回答にリンクを既に提示してあります。 BITMAPINFOHEADERから読み取れる幅の単位はピクセルです。 24bppのビットマップの場合1ピクセルは3バイトです。 よって横幅の最小バイトは3*widthバイトとなります。 ここから削ったら右端が消えてしまいますので、ダミーデータ(バイト)を"増やし"ます。
carnage0216

2018/03/04 09:54

アセンブリプログラムの無駄な部分があるにしてもなぜ無駄なのでかを理解したいです。 というか質問には載せていない前の3行の分の mov wMargine, 0 mov eax, width /* 幅 */ and eax, 0x00000003 /* %4 */ の部分も「正しい」理解が出来ているかわかりません。3行目の0x00000003 をeaxに入れるのもモノクロに変換するためのカラーデータのRGBデータを収めるための入れ物として扱うためにeaxに0x00000003 をandしたのではないかと考えています。間違っているかもしれませんが。
asm

2018/03/04 09:57

> mov eax widthという命令 アセンブラ次第ですが、おおむね変数もしくは定数だと認識していました。
asm

2018/03/04 10:00

> 3行目の0x00000003 をeaxにいれる いえ、これはC言語で表現すると eax &= 4; もしくは eax %= 4; です。
carnage0216

2018/03/04 10:21

あれ?この0x00000003は整数ではないのですか? 0x00000003は10進数かと思っていたのですが、なせ4と置けるのですか?
carnage0216

2018/03/04 10:22

0x00000003が整数の3ではないならばRGBのデータを扱うために3をandしたのではという解釈は大間違いです。
asm

2018/03/04 10:22

あ、ごめんちょいミスったね eax &= 3; もしくは eax %= 4;
carnage0216

2018/03/04 10:37

ってことは0x00000003は整数であり4でないのですね? だとしたらand  eax, 0x00000003 /* %4 */と書くのはややこしくないですか? 普通にand  eax, 0x00000003  /* %3 */と書いたほうがわかりやすいのではないでしょか?① あるいは3をかけてからeaxレジスタに入っている値を4で割るため and  eax, 0x00000003 /* %4 */とかいたのでしょうか?② だとしてもwidthに3を掛けるアセンブリ命令はmulだったはずですがandをなんで使っているんですか? 個人的には mov wMargine, 0 mov eax, width /* 幅 */ mul eax, 0x00000003 /* %4 */ div eax, 4 になるような気がしますが、まあ、4で割り切れずに余りが出る場合を考えて3を掛けているんでしょうが、しょっぱなから4で割って出てきた余りを求めるだけでいいと思います。 なんでわざわざ3を掛ける必要なんてないのに、と思います。どう思いますでしょうか? 高速化のためにしているとは思えません。③ 以上の3つの疑問がありますがどうかよろしくお願いいたします。
asm

2018/03/04 11:35

とりあえず回答だけに答えると ① いいえ。C言語の%演算子と論理積は別物です ② いいえ。以前にも申したとおり論理積は普通の掛け算とはちがいます ③ 単位変換のためです。 誤解によって生じる誤った疑問のみを訂正しても納得することができないのでしょうね。 基礎から教えるにはこのコメント欄はあまりにも狭すぎますので、新回答でも書いてみます。
episteme

2018/03/04 12:38

> and  eax, 0x00000003 /* %4 */と書くのはややこしくないですか? なんで? 3とANDすることで”%4してるのよ"とコメントしてるんしょ?
carnage0216

2018/03/04 13:10

widthの入ったedxに3を論理積(and)することで%4が出てくる計算過程がわからないのです。 %を調べてみても検索文字として認識されません。 0x00000003が %4 とうことですか? epistemeさんのおっしゃる%4しているよの意味が分からないのです。
carnage0216

2018/03/04 13:17

eaxレジスタに入れたwidthに0x00000003を足すことで4で割った際の余りが出ることはわかりました。 0x00000004を足してしまった場合は、足した分の4も割るため時間がかかるとかで高速化に関して意味がないのはわかります。そのため0x00000003を足したのでしょう。 しかし、eax, 0x00000003の横に書いてある%4の意味が何を意味しているのかさっぱりわかりません。
rubato6809

2018/03/04 14:10 編集

%がわからない!? %はC言語の剰余を求める演算子です。たとえば、13 ÷ 4 = 3 ... 1 。余り1ですから、13 % 4 = 1 です。 基礎から再確認が必要かな。 > 0x00000003は整数であり4でないのですね? 0x00000003が16進数の3、即ち 10進数でも3なことは、C言語で経験しませんでしたか? > and eax, 0x00000003 /* %4 */と書くのはややこしくないですか? ややこしくありません。むしろAND命令の目的を明確にするコメントです。つまり、4で割った余りを求めているのだ、とコメントしている。 まず、AND演算(AND命令)がどのような動作をするのか、あやふやなら、このページの「図6-1.8ビットのAND演算」で確認してください。 http://sevendays-study.com/computer-literacy/im-day6.html 次に、AND命令は大抵ビットマスクに使われます。 3 = 00000011 (2進数) = 2^2 - 1 との論理積(AND)は下位2bitをそのまま残し、それ以外のビットをマスクします(0にします)。 例えば、0b1101 & 0b0011 == 0b0001 (13 & 3 == 1)。 さらに、a % 4 == a & 3 が成り立ちます。例えば 13 ÷ 4 = 3 余り 1。 一般化すれば「2のn乗で割った余り == (2のn乗 - 1) とのAND演算」です。これは2進数で理解できる規則の一つといえるでしょう。いろいろ試してみてください、 でも、この理屈がわからなければ、、、う〜ん、どうしましょうか笑。 > widthに3を掛けるアセンブリ命令はmulだったはず はい。そうです。 > andをなんで使っているんですか? and 演算は、3を掛ける掛け算ではなく、4 で割った余りを求めるために行っています。 EDX:EAX = width * 3 (mul %ebx) を求め、次に EAX = EAX & 3 (and eax, 3) で 4 で割った余りを求めています。同じ「3」が使われていても、たまたま同じ値なだけで、異なる演算です。一つ一つ段階があるのですから、混乱しないように。 > なんでわざわざ3を掛ける必要なんてないのに、と思います 私が「乗算も減算も必要ない」と書いたことを言っているなら、やはり私の言葉を鵜呑みにしただけで、なぜそう言えるのか、その意味を理解できていないのです。 質問者が計算の目的や根拠がわかっていない段階で、必要ない計算だと余計な事を書いたので混乱させたわけです。申し訳ない。 まず、hmmmさんのコメント 「求めたいのはパディングする長さ…最終的に 4 - (4で割った余り) となるのはおかしくない」ことを理解する必要があります。 そもそも 4 - (4で割った余り) を求めることは必要なのです。 そのためには、3倍することも、4で割った余りを求めることも、その余りを4から引くことも、本来は必要なことなのです。必要なことを素直にコード化したのです。 ただ、たまたま?この場合は 4 - (4で割った余り) を求めなくても済む場合だっただけで、常に不要だとまでは言えないと思いますよ。
rubato6809

2018/03/04 13:30

> widthに0x00000003を足すこと > 0x00000004を足してしまった場合は、足した分の4も割る あの〜、そこで足し算(ADD命令)は使われていませんよ。あなたにとって、足し算も掛け算もAND演算も、どれも同じ「足し算」なのですか???
carnage0216

2018/03/04 13:46 編集

%がわからない!? あの、割り算を表してあることはわかります。 しかし、and eax, 0x00000003 と書いてあり論理積を表しているのに/* %4 */とかくのはどう考えてもおかしいじゃないですか? 3は二進数で11なのでwidthの下二桁の状態を知るために論理積をしたのですよね? そして二進数11をwidthに論理積をしてから4で割ることを表すために/* %4 */と書いたのかなと思ったのですが、 11を論理積したwidthを4で割るんだったら and eax, 0x00000003 /* 二進数11をeaxに論理積する */と書いた後で、 割り算命令のdiv eax 4 /* %4 */ と書けばいいじゃないかと思いました。 なんでわざわざわかりにくく書いているのかわかりません。 私がちゃんと用語を正しく使用していなかったため間違った情報を伝えてしまいました。 本当に申し訳ありませんでした。
episteme

2018/03/04 13:43

> and eax, 0x00000003 と書いてあり論理積すなわち足し算を表しているのに/* %4 */とかくのはどう考えてもおかしいじゃないですか? ...論理積は足し算じゃないよ? AND 3 すると下位2bitを残してそれ以外全部0にする、つまり4で割った余りとなる。
rubato6809

2018/03/04 13:45

あのねえ、 > 論理積すなわち足し算を表している ??? 論理席が足し算だって???めちゃくちゃな文章書かないでほしい(呆) 質問を書きなおしてください。あなたが何を理解できて、何が理解できないのか、コミュニケーションできないなら何をやっても無駄だ。 もし、「論理席すなわち足し算」と書いて問題無いと思うなら、私は相手したくない。
carnage0216

2018/03/04 13:52

本当に申し訳ありませんでした。私が誤った理解をしていました。 たまにandと足し算を同じように扱ってしまう癖があり大変失礼な発言をいたしました。 言い訳をしてしまい申し訳ありません。
carnage0216

2018/03/04 14:01

rubato6809さん、epistemeさん。 正しい理解が出来ていないままめちゃくちゃな質問をしてしまい本当にすいませんでした。 呆れられて当然です。ただ、自分でいうのもすごく悲しいのですが、これでも勉強をしているんです。 人よりも学習に時間はかかるためこのような過ちを起こしてしまいます。 ですが、どんな言い訳であっても質問をした私に責任があります。 私も人間であるため今回のような幼稚な過ちをしないとは言い切れませんが、どうかこれからもよろしくお願いできないでしょうか?
carnage0216

2018/03/04 14:04

今更で言い訳にしかなりませんが、論理積と足し算は全くの別物であります。 教えていただきどうもありがとうございます。
carnage0216

2018/03/04 14:31

rubato6809さんに質問なのですが、ある方は最終的に 4 - (4で割った余り) となるのはおかしくないです。とのことですが、自分は例えば4で割った余りが1だった場合、4-1となりパディングとして使われるダミーデータは3となります。4で差し引く過程がなくても1~3のいずれは出ますし、4で差し引く場合でも出てくる順番が違うだけで1~3のいずれかが出るので、結果としてrubato6809さんと同じように最終的に 4 - (4で割った余り) となるのは必要ないというかやる意味がないということでおかしいと思うのですが。rubato6809さんは以前と同様に4を差し引く過程はいらないという考えなのでしょうか?
rubato6809

2018/03/04 16:52 編集

あなたは何か勘違いをしていると思うけど、もしかして、こういう数の扱いに長けているか、どちらでしょうか。。。 私は、もちろん漠然とした知識はありますが、実際にビットマップデータを操作した経験はありません。示されたアセンブリコード自体は100%理解できるけど、そのコードが計算した wMargine 値がどう使われるのか、わかりませんから、私は明確なイメージを持てませんでした。 でも「求めたいのはパディングする(=端を0で埋める)長さ」ということなら合点がいきます。24bit bmp であれば、1ドット当り3バイトのメモリを使うのだから、 width * 3 (=横方向のバイト数)を計算するのは当然。そしておそらく、そのメモリ領域を4バイト単位で扱うと、ハードウェア的 and/or ソフトウェア的に都合が良いのでしょう。だからパディングするのです。 ここで念の為整理したいのですが、wMargine の値として求めているのは、 4 - (width % 4) ではありませんよ。 4 - ((width * 3) % 4) です。 示されたコードの先頭で width % 4 を求めています。width が4の倍数なら、3倍しても4の倍数のまま・・・これは簡単に判断できることですが、wMargine を求める計算には「 * 3」があります。 4で割りきれないとき、3倍して、その % 4 を求め、さらにその値を4から引く・・・これを見て、width % 4 に一致すると判断できる人は、そうはいないはずです。 私は表を作れば簡単な規則が見いだせるに違いないと考えて、表を作ってみて、一致することがわかったけど、即ち、表を作らないうちは規則がわかりませんでした。 あなたは、「そんな事、すぐわかるのに・・・」とおっしゃりたいのですか? そういう人もいるでしょうが、圧倒的に少数派でしょうね。
carnage0216

2018/03/04 17:01

widthが変数にしても定数にしても、3を掛けて4を引けば必ず、4-(width % 4)と順番は違えど結果として得られるものは同じだとわかりました。 なので載せたアセンブリプログラムでは 4 - ((width * 3) % 4) と計算するように書いてありますが、 私は4 - (width % 4) でもパディングとして扱われるデータの数の総合は4 - ((width * 3) % 4)と同じなので、プログラムを短く書いて無駄を省くならば4 - (width % 4) のほうがアセンブリプログラムも短くていいと思いました。
carnage0216

2018/03/04 17:02

回答どうもありがとうございます。 今後は気を付けます。
rubato6809

2018/03/04 17:08 編集

> 3を掛けて4を引けば必ず、4-(width % 4)と順番は違えど結果として得られるものは同じだとわかりました それを、どうやってわかったの? > 4 - (width % 4) のほうが短くていいと思いました wMargine = 4 - (width % 4) で計算すれば良い、と言ってるの?
carnage0216

2018/03/04 17:09

wMargine = 4 - (width % 4) でも 4 - ((width * 3) % 4)でもいいと思います。 しかし、自分が書くとしたら4 - (width % 4) を書きます。 私も計算は苦手なのでまた間違ったことを書いているかもしれません。 間違っていたら申し訳ありません。 間違っているなら間違っていると指摘していただけるとありがたいです。
asm

2018/03/04 17:11

順番が違ってたら意味がないんだよなぁ… width=5の場合 4-(width*3 %4) = 1 4-(width%4)=3
rubato6809

2018/03/04 17:13 編集

> wMargine = 4 - (width % 4) でも 4 - ((width * 3) % 4)でもいい あきらかな間違い。あなたのレベルにネット越しで教えるなんて、もういやだ。
carnage0216

2018/03/04 17:18

? あの、埋めるだけのダミーデータに順番は必要だったんですか?順番が必要だとしたら私は大きな間違いをしました。 てっきり、パディングとして扱うデータの量、えーと埋めるだけのダミーデータの総数が合っていればいいと思っていました。 またやらかしてしまった…。
asm

2018/03/04 17:20 編集

wMargine はダミーデータではありません。 あなたの言うダミーデータの量です。
carnage0216

2018/03/04 17:54

どうも不甲斐ないです。 私の低レベルな間違いのせいでまたやってしまいました。 rubato6809さん、どうも今まで回答してくださりありがとうございました。
carnage0216

2018/03/04 17:56

asmさん、私の総数うんぬんであっている思っていましたが間違っていたため、ダミーデータに関する解釈も間違っていました。 どうも苦労をおかけしました。
carnage0216

2018/03/04 18:04 編集

ほんとに自分の学習力の低さに嫌気がさします。
carnage0216

2018/03/04 18:10

wMargine = (width % 4) でも 4 - ((width * 3) % 4)でもいいと思います。 しかし、自分が書くとしたら (width % 4) を書きます。 今となっては後の祭りですが4-を前者の式に付け足していました。 でもまさか順番が逆になって総数が同じになるかと思ったら、同じ順番通りに綺麗に数字が互いに一致することは全然わかりませんでした。 すぐにわかった人は凄いですね。
asm

2018/03/04 18:21

最適化をしていない怠惰なコードに利点が無いわけじゃないです。 ビットマップは24bppの他に32/16/8/4/1 bppがあり (bpp = bit per pixel) 24/16/8 bppについては、ほぼ処理を一緒にする事ができます。 ビットマップの保存/ロード毎に1回しか使わない処理なので、ボトルネックになることは稀でしょう。 FPGA等で専用回路作る等した場合には問題になりえますが… その場合、横幅は固定/半固定になるでしょうし事前ないしは1度の計算で使いまわせます。
carnage0216

2018/03/04 18:24

最適化をしていない怠惰なコードに利点が無いわけじゃないです。に関して下のbitなどの情報も読んでみたのですが、ごめんなさい。 さっぱり頭が追い付きません、、、、、。 FPGAに関しては少し理解できました。
episteme

2018/03/04 22:51 編集

> wMargine = (width % 4) でも 4 - ((width * 3) % 4)でもいいと思います。 ...なんでやねん。 1%4 = 1 だが (1*3)%4 = 3 だぞ? [追記] ごめん、コレに関してはあなたのいう通りだ。
carnage0216

2018/03/04 20:37

epistemeさん質問いいでしょうか? 画像の左端が4の倍数だと扱いが楽(=高速)なんですわ。とのことですが、どうやって4の倍数が画像の左端にいくようにするのですか? 画像の左端とはピクセルの画素、えーと24bitを4の倍数に指定するということですか? だとしてもアセンブリプログラムで計算はできても、ジャスト左端に四の倍数のデータを入れられるだろうかと思うのですが? 方法はあるのでしょうか? 画像の横の長さをバイトあるいはbit単位で計算でもとめるとかの方法ですか?
episteme

2018/03/04 22:39

> どうやって4の倍数が画像の左端にいくようにするのですか? だからー...そうなるように無駄なパディングをいくつか(0~3byte)差し込んでおきましょう。っていう画像フォーマットの仕様なんでしょ? で、この質問はこのフォーマットを満たすのに必要なパディング長を求めるものじゃなかったんすか? # 「横幅(pixel数)wが与えられたとき (3w+p)%4=0 となるpを求めよ」  ならば答えは結局のところp=w%4です。
carnage0216

2018/03/05 00:05

「横幅(pixel数)wが与えられたとき (3w+p)%4=0 となるpを求めよ」  ならば答えは結局のところp=w%4です。 凄くわかりやすいです。 どうもありがとうございます!!
carnage0216

2018/03/05 00:07

あの本書のCPUの構造がわからないので同じような結果になるかはわからないのですが。自分なりにコードを書いてみました。結果としては同じような結果になると思いますでしょうか? mov eax, width  // eax = width; mov edx, 3  // edx = 3; mul edx   // edx:eax = eax * edx; がもともとのコードです。 以下が私の書いたコードです。 mov eax, width mov ebx ,3 mul eax ,ebx //←こう書いた場合は掛け算の結果はeaxに入るのでしょうか?// mov ebx ,eax //eaxの内容をebxに入れます。// どうかよろしくお願いいたします。
episteme

2018/03/05 00:14 編集

なにをお願いしてんだ? 各命令がナニするものかはレファレンスにあるんだろ?
rubato6809

2018/03/05 01:02

> 自分なりにコードを書いてみました。結果としては同じような結果になると思いますでしょうか? 何を甘えているのか。自分の手で確かめよ。自分で書いたコードは自分の手で動かすのがプログラマである。テストもデバッグも自分で行う。目の前にはパソコンがある。紙とペンもある。資料もある。参考書もある。ネットには豊富な情報がある。自分の手と頭を動かして確かめられることはいくらでもある。うまくいかない・できなくても、工夫の余地はある。考えよ。 さっさとアセンブル・コンパイルしてみたらどうか。あなたはまだ手を動かしていない。すぐにもできることをせず、ここに聞けば教えてもらえると思っていたら、どこまで行ってもわからないことだらけだ。 天は自ら助くる者を助ける。
episteme

2018/03/05 01:10 編集

↑それな。 口開けて待っててもホコリが入るだけだ。 欲しいものに手を伸ばせ。畳の上の水練はそのくらいにしてそろそろ水に飛び込め。
episteme

2018/03/05 02:47

回答追加しといた。これで飛び込めるぢゃろ、せいぜい足掻くがよい。
carnage0216

2018/03/05 12:06

甘えていました。 紙に図を書いたりしてはいましたが、アセンブルする事を忘れていました。大事な事を忘れて頼り過ぎました。 自分でやってみた時に失敗するのが怖くて皆さんを頼っていました。 僕はプログラマにはなれませんが、今までに教えて頂いた知識を活かします。
carnage0216

2018/03/05 12:10

あの、rubato6809さん、epistemeさんは自分だけでは解決できない問題に出くわした時、どうしているのてすか? 解決するまでひたすら自分で追い続けるのですか?
episteme

2018/03/05 12:16 編集

考えて/調べて/やってみてわかりそうならそうする。 知ってなきゃどーにもならんなら訊く。 そのお礼に知ってることは教えるし、僕の方が簡単に調べがつくなら調べて/試してやんよ。もらってばっかりじゃ悪いし、もらえなくなるからね。
carnage0216

2018/03/05 12:28

かっこいいですね。 お世辞とかではなく。 凄いと思いました。
rubato6809

2018/03/05 12:31

あと、気軽に相談できる相手・仲間は大事だ。学生時代は類友な仲間がいたし、就職してからは同僚がいたから互いに刺激しあい助けあうこともあった。
episteme

2018/03/05 12:31

そお? フツーそーすんじゃないの?
rubato6809

2018/03/05 12:33

ここで悩んでる若者達に、孤独な感じがあるので、特に強調してみましたw
carnage0216

2018/03/05 12:36

rubato6809さんの環境が羨ましいです。 もちろん、すごい努力と才能があったと思います。
rubato6809

2018/03/05 12:36

って、俺に言ったんじゃないのか笑>epistemeさん
episteme

2018/03/05 12:38 編集

それは同感。こんなトコで訊きまくってるよかツレとやいのやいのやってるほうが万倍理解できるしタメになるかんねー。 # ぃゃぃゃ double-meaning ってことでww
carnage0216

2018/03/05 12:54

画像の左端が4の倍数だと扱いが楽(=高速)なんですわ。 に関して式は教えて頂きました。 高速化に関する質問なのですが、 今実際に行ってみて推測したのですが、画像を読み込む時は左上から右へ、そして右端に着いたら、一段下の左上にと読み込んで行くと処理内容からわかりました。 epistemeさんが左端は4の倍数のバイトだと処理しやすいのはrgbのそれぞれの成分がr,g,bがそれぞれ8ビット、 すなわち、rgbが3バイトであり、隣のrgbのrの成分8ビット(1バイト)を加えて4バイトになるため高速になると処理内容からみて考えました。 隣のr成分を読み込むことで次のgb成分を読み込みやすくなるのではないかと思いました。 僕の考えは間違っていますか? 確認として聞きたいのです。
carnage0216

2018/03/05 12:55

rubato6809さんとepistemeさんは大学で勉強なされていたのですか?
episteme

2018/03/05 22:59

してないわけじゃないけど、ホント基礎のとこだけ。あんなんじゃ全然足りないのは明らかで。
episteme

2018/03/05 23:44

> 高速化に関する質問なのですが... 推測でしかないけど、グラフィック絡みはスピード命なんよね。 画像の1lineを描く際、左端から32bit読み出してそのうちの24bit使って1pix.残る8bitと次の32bitを繋いで24bit使って1pix...ってなチョーシで描いていく。 で、右端に到達したら次のlineを描画するけど、ここでpaddingされていれば↑と同じダンドリで描画できる。どのline描くのもダンドリは同じ。 ってことじゃネーノかな?
asm

2018/03/05 23:48

> 画像を読み込む時は左上から右へ 一般的なビットマップは左"下"からだけど、上です? > 4バイトに合わせる理由 グラボにメモリを転送する時にDMAみたいな感じで何かあるんじゃねーかと予測してます。
episteme

2018/03/05 23:52

あー...そーいや下からやね。なんでか知らんけど。 そか、DMAか。PCI経由でグラボに投げる際staging-areaに転送される境界がハンパでは困るか。
carnage0216

2018/03/06 06:58

epistemeさん、asmさん。 貴重な御解答、どうもありがとうございます。 「そか、DMAか。PCI経由でグラボに投げる際staging-areaに転送される境界がハンパでは困るか。」に関しては素人の僕にはさっぱりですが...。
episteme

2018/03/06 07:53

んー...すっげー"ざっくり"言うと: 画像データをグラボに転送せにゃ描画してくれんでしょ。 そんときたとえば for-loop とか memcpy みたいなの使ってちゃ遅くて埒開かんです。なのでCPUが関与せずに裏でこっそり高速に転送する"からくり"が用意されてて、それがDMA(direct memory access)。DMAには転送元/転送先を指定するんだけど、そのアドレスが32bit境界ピッタシじゃなきゃいけないとすると説明つくぢゃん! ってことです。
rubato6809

2018/03/06 07:54

> 素人の僕にはさっぱり なら、程々のところで収めて、あまり深追いしないこと。<全てを理解しようとしてはいけない>。 実際、ここにいるオッサン達(俺を含めてw)も明確な説明ができないのは、おそらくハードウェア絡みの事情があり、それはソフト屋にとって領分の違う問題だということ、そしてソフト屋はハードウェアに関して精密に理解しなくても通用しているという事に気づくべきだ。ハードウェアも理解できるに越したことはないが、ハード屋さんと同じレベルでハードウェアを理解しようとしたら、ハード屋さんに笑われますよ。 ともかくここで理解すべき事は、画像に限らず「境界がハンパでは困る」ことがよくあること、そのためソフトウェアで境界合わせをすることは結構多い、なのでソフト屋はああしたコードに慣れっこになっている、AND命令で剰余を求めるなんていうのもその中の小技の一つ。 境界合わせなんだとわかれば、あの程度のコードはすぐ理解できなくちゃいけないし、アセンブリ言語をわかりたいなら、一命令毎の意味をきちんと押さえる必要があります。そこはソフト屋の領分。
episteme

2018/03/06 08:00

んなわけで、こんな"こゆい"ネタに反応できる回答者の多くは間違いなくオッサンです。 # そろそろ定年が見えてきたなァ...
carnage0216

2018/03/06 08:13

そうなんですか‥‥。 でもこゆいネタも私にとっては大変勉強になります。 解説、どうもありがとうございました。
asm

2018/03/06 08:57

もしくは単に「先頭32bitに合わせてサイズも32bitに合わせる事を約束しておけばmemcpyが速い」ってだけかもね なんせwindows黎明期のフォーマットなので今とは事情が異なる可能性も大いにあります。
guest

0

解決済ですが、diracpaulさんへの質問者のコメントには答えたいことがあります。

載せたアセンブラプログラミング以外のアセンブリ命令の組み合わせでも同じような処理を実現できるのでしょうか

アセンブリコードには決まりきった命令順(イディオムも)もよく出現しますが、そもそも命令の粒度が細かいので、人によって・コンパイラによって、同じ処理でもコードの違いは大きくなります。以下、コメントにC言語風の擬似命令を併記しながら解説します。

xor edx, edx // edx ^= edx; mov edx, 0 // edx = 0; sub edx, edx // edx -= edx;

既にご存知でしょう、これは3つ共にレジスタedxの0クリアが目的ですから、
mov edx, 0 が自然ですが、これだと32bitの0という値を機械語に持つため、機械語命令が長くなる・遅いかもしれない(アセンブルリストで機械語の違いを確認しましょう)、そこで2進数の性質を利用してxor命令がよく使われます。

edxには乗算の結果が格納されることから、おそらく安全のため0クリアした・・・宣言した変数はすべて0に初期化するのと同じ気持ちなのでしょう。でもmul命令の仕様がわかっていれば、edx を0にする必要が無いことは明らか。即ち、xor命令は削除できます。

次に乗算命令の箇所。

mov eax, width // eax = width; mov ebx, 3 // ebx = 3; mul ebx // edx:eax = eax * ebx;

32bit * 32bit の計算結果は 64bit になります。mul命令の結果が EDX:EAX に格納される(EDX, EAX レジスタを固定的に使う)ことから、乗数を EDX に入れて乗算させることが多いのではないでしょうか。レジスタは限りある資源なので、関数の中で使うレジスタを減らした方が何かと有利なのです。
どうせ mul 命令は edx を使うのだから、私ならここは edx を使い回します。ebx を使わずに済む事は大きいのです。こんな風に

mov eax, width // eax = width; mov edx, 3 // edx = 3; mul edx // edx:eax = eax * edx; ... // 途中省略 mov edx, 4 sub edx, eax // edx = 4 - (width * 3) % 4;

それはともかく、3倍は同じ値を3つ足してもよいので、例えばこうできる。

mov eax, width // eax = width; mov edx, eax // edx = eax; add eax, edx // eax += edx; // width * 2 add eax, edx // eax += edx; // width * 3

mulは時間がかかる命令ですから、一命令増えても実行速度は断然速いです。
じゃあ10倍するには10回加算を繰り返すかというと、そうではなくて、2のべき乗(2, 4, 8, 16 ...)倍にシフト命令を使えることをご存知でしょう。10倍は8倍+2倍だから、10倍するマクロは

#define MultBy10(x) ((x) << 3 + (x) << 1)

のように、シフトと加算の組み合わせで実現できます。もっとも今のコンパイラは何もしなくても大抵こんな風に最適化しますが、アセンブリ言語ならどう書けばよいか、質問者への課題にしましょうか笑。

さて、ここが重要。
提示されたアセンブリコードは何を計算しているのか。全体をC言語風に書けば、こうなります。

C

1 wMargine = 0; 2 if ((width % 4) == 0) goto no_margin; 3 wMargine = 4 - ((width * 3) % 4); // この計算は何か?

width から wMargine を求めていることがわかります(と共に処理を分岐している)が、width % 4 は 0, 1, 2, 3 しかなく、0の場合は明らかなので、0以外を場合分けして考えると、計算結果はこうなります。

width%4widthwidth*3(width*3)%44-(width*3)%4
14n + 112n + 331
24n + 212n + 622
34n + 312n + 913

「width % 4」と「4 - ((width * 3) % 4)」が一致する!・・・実は乗算も減算も不要、即ちあの部分は無駄なコードなのです(あくまで提示されているコードを見る限りですが)。つまり、次の5行だけが残る。

mov wMargine, 0 // wMargine = 0; mov eax, width // eax = width; and eax, 0x00000003 // eax %= 4; jz no_margin // if (eax == 0) goto no_margin; mov wMargine, eax // wMargine = eax;

さらに、x86はmov命令でフラグレジスタが変化しないので、次の4行まで絞り込める・・・

mov eax, width and eax, 0x00000003 // eax = width % 4; mov wMargine, eax // wMargine = width % 4; jz no_margin // if (eax == 0) goto no_margin;

このような「無駄」はアセンブリ言語に限った事ではありません。言い換えれば、漫然とコードを書くなら、どんな言語を使っても無駄な計算をしたり効率が悪かったりするという戒めになるでしょう。


wMargine = 4 - ((width * 3) % 4); // この計算は何か?

念の為、この部分はどういうコードであったか。C言語風コメントをつけておきます。

xor edx, edx // edx = 0; (削除可能な命令) mov eax, width // eax = width; mov ebx, 3 // ebx = 3; mul ebx // edx:eax = eax * ebx; 即ち edx:eax = width * 3; and eax, 3 // eax &= 3; は eax %= 4; と同じ mov ebx, 4 // ebx = 4; sub ebx, eax // ebx -= eax; 即ち ebx = 4 - ((width * 3) % 4); mov wMargine, ebx // wMargine = ebx;

投稿2018/03/04 08:05

編集2018/03/04 16:37
rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

carnage0216

2018/03/04 08:58

回答してくださりどうもありがとうございます。
carnage0216

2018/03/04 09:33 編集

rubato6809さんは何者なのですか? どこかのIT企業でお仕事なされているエンジニアの方なのでしょうか? 仕事とは別に趣味であったとしたらレベルがとても高いプロのようです。 差し支えなければ教えていただけないでしょうか?
carnage0216

2018/03/18 19:53 編集

今更ながら申し訳ありません。復習をしているのですがwMargine=ダミーデータという扱いでよいのでしょうか? どうかよろしくお願いいたします。
carnage0216

2018/03/18 22:55 編集

ダミーデータを求める計算式は4-(width*3)%4でも問題ないとのことですが、1ピクセルが3バイトでありwidth自体がRGB3バイトを含んでいるのだとしたらwidth*3と書いてしますと、RGBを含んでいるwidthと掛けたRGBが重複してしまう気がするのですが、 個人的には本を読む限り、width=(n個の横のピクセル)*(3バイト/1ピクセル)を表しているように思えます。width/4の4はRGBが3バイト故に4でバイト単位でwidthを割っており、4バイトで高速化を考えているため、widthの余りのとなった1~3バイト数のRGBはダミーデータとして扱われる。 以上のことからアセンブリプログラム内にwidthをどのように扱うか定義されていないところを見ると、 ダミーデータを求める計算式はwidth/4と書くのが正しいものであり、本に書いてある4-(width*3)%4の方はたまたま、3を掛けたらwidth/4と同じダミーデータのバイト数を得られただけだと考えています。 皆様はどのように考えるかを解答していただけると大変ありがたいです。 どうかよろしくお願いいたします。
carnage0216

2018/03/18 22:30

なので、「実は乗算も減算も不要、即ちあの部分は無駄なコードなのです」に関しては、無駄ではなく、間違ったコードなのではないかと考えています。 もしかしたら作者なりに都合よくなるようにするために書いたコードなのかもしれませんが。
rubato6809

2018/03/20 22:24

carnage0216さん 別の質問(118057)で議論が続き、03/19 07:30 からちょうど2日間たちました。放っておくのも何なので、お伺いします。 > 無駄ではなく、間違ったコードなのではないか というお考えは、同じですか、変わりましたか? 変わったなら、どう変わったか、整理できますか? こちらは急ぎませんので、じっくり考えて、お返事ください。
carnage0216

2018/03/21 00:46 編集

おはようございます。考えはかわりました。 なので、間違ったコードではないことを証明したいと思います。確認をよろしくお願いいたします。
carnage0216

2018/03/21 01:20

私なりにまとめたので見て頂きたいと思います。 式が4-(width*3)%4となる理由を説明したいと思います。 例えばwidth=15バイトだとします。 そして3を15バイトかけるのはrgbデータを収納するためのメモリを確保するためです。 (15*3)バイトを4バイトで割るのは(15*3)バイトの中にいくつの4バイトがいくつあるかを調べるためです。 そして(15*3)%4より、4バイトは11個ある事がわかり、1バイト余る計算となります。このおしりの1バイトを4バイトにするには式4-(width*3)%4の4からおしりの1バイトを引けば、1バイトに何バイト足せばよいかわかります。そのため、式に4-を入れました。 よって4バイト-1バイト=3バイトより、おしりの1バイトに3バイトを足せば4バイトとなり、横幅は4の倍数のバイトとなります。 以上より参考した書物に書いてある式が4-(width*3)%4である理由が証明できました。 理由あっての式4-(width*3)%4が正しい式だと理解できました。
carnage0216

2018/03/21 02:47

4-(width*3)%4=width%4 (width≠4n)に関して証明したいと思います。 変数が一つの式ならば、どんな式も=できるのですが。 今回はwidth=4n+1、4n+2、4n+3とwidth内に変数があるので二つの変数を使っての計算となるため、width≠4nなどの例外が起きる。 4-(width*3)%4=width%4 (width≠4n)とできるのは剰余の分配法則を使ったためであります。 width%4 のwidthにwidth=4n+1、4n+2、4n+3を代入しても剰余の分配法則より必ず余りが出る 4-(width*3)%4のwidthにwidth=4n+1、4n+2、4n+3を代入しても剰余の分配法則より必ず余りが出る その余りの数値が=で結べたため簡略化できたと私は言えるのではないかと思いました。 ただ、証明しておきながらwidth%4 の式に違和感があります。4-(width*3)%4のwidth*3ではRGBの3バイトのメモリを確保するために3をxに掛けていますが、width%4の式ではwidthに3はかけていないのでRGBのメモリを確保できないとは言いませんが、ただ、メモリを確保しているだけなのかなと思ってしまいます。
carnage0216

2018/03/21 02:49

なので、簡略化も良いと考えますが個人としては本に書いてあった4-(width*3)%4かasmさんに勧めていただいた(width*3 +3) & -4のどちらかが素直で私には理解しやすいです。
carnage0216

2018/03/21 02:57

個人として考えたのは3*x%4と4-(3*x%4の余り)の二つの式を用いてwidthを埋めるパディングを求めます。もしかしたら式を二つにしたりでデータのサイズが大きくなったりしますが、まずは自分の納得のいくコードがかけてから高速化をはかります。
carnage0216

2018/03/21 08:16

4-(width*3)%4をwidth%4 に変換できるゆえに、width%4にはwidth*3のrgbのメモリを確保する意味合いが含まれているのかもと。 やはり、わたしにはwidth%4に関しては本当は申し訳ないですが、前の質問で書いた式の方が今の私には理解しやすいです。 ごめんなさい。
carnage0216

2018/03/21 12:37

あの、私の証明は間違っていたのでしょうか? 返信がないので間違っているのではと不安です。
rubato6809

2018/03/21 13:28

あれは証明とは言わないでしょう、「説明」ですかね。 それも含めて、今日はコメントを書けないので、気長にお待ちください。
carnage0216

2018/03/21 13:50

てっきり とうとう呆れられて返信すらしていただけなくなったのかと思い、頭が真っ白になりかけました。 そうだったのですね。よかったです。気が短いものですいません。 では待たせていただきます。
rubato6809

2018/03/23 10:12

いろいろ言いたいことがあるけど、まずはここから。 > (15*3)%4より、4バイトは11個ある事がわかり、1バイト余る 45 % 4 を計算したら「11個ある事がわか」るでしょうか?
rubato6809

2018/03/23 11:39 編集

C言語のプログラムで % 演算子を使ったこと、無いでしょ?例えば main() { int w = 15; printf("%d\n", (w * 3) % 4); } 11は、どこかに表示されるかな?
carnage0216

2018/03/23 11:45

main() { int w = 15; printf("%d\n", (w * 3) % 4); } printfのカッコ内にある"%d\n"の%dに11が表示されると思います。
rubato6809

2018/03/23 11:47

思います、ではなく実行してから返事しなさい。
carnage0216

2018/03/23 12:02

実行してみたところ1と出ました。
carnage0216

2018/03/23 12:04

何かの間違えかと思いint w=14の時で実行したところ2とでました。これは割った余りを表しているのですね。 main() { int w = 14; printf("%d\n", (w * 3) % 4); }
rubato6809

2018/03/23 12:08

もう一度聞く。 45 % 4 を計算したら11個ある事がわかりますか?
carnage0216

2018/03/23 12:15 編集

もちろんわかります。 45バイトを4バイトで割ったら4バイトが11個あり、1バイトと余るのですよね。 ちゃんと実行しました。なのに11とはでず、1とでます。なんで余りのほうが出るのか調べているのですがまだ解決していません。
carnage0216

2018/03/23 12:29 編集

#include <stdio.h> main() { int w = 15; printf ("%d\n", (w * 3) ); int i = (w * 3); printf ("%d\n", (w * 3)/4 ); int a = (w * 3)/4 ; printf ("%d\n", (w * 3)%4 ); } を実行したところ 以下のように出ました。もちろん理解できた通り、11バイトあまり1バイトとでました。 45 11 1
carnage0216

2018/03/23 12:30

なるほど、「%」だとあまりが 「/」 とすると11を得られるのですね。 勉強不足でした。申し訳ありません。
carnage0216

2018/03/24 04:57

あの、不快な解答をしてしまい申し訳ありません。 私の回答は間違っているのでしょうか。 %を/にしたところちゃんと4バイトが11個あることがわかりました。
guest

0

寝ておきたらほぼ終わってたが、とりあえず書いたので

基礎の基礎

1bitとは

2進数1桁の事であり、0か1のどちらかの状態を保存する記憶領域です

1byteとは

(2008年以降)標準的には8bitです。


論理積とは

まずは1ビットの論理積

左辺 & 右辺 = 解

左辺右辺
000
010
100
111

という1ビットの演算です
1ビットが入力され1ビットが返される演算の事を論理演算といいます
今現在のコンピューターというのは突き詰めるとこういった論理演算で全てが実装されています。

次にビット毎の論理積

これを拡張して8bitの演算を考えます
例: 101 & 15

101 = 0x65 = 0b01100101 15 = 0x0F = 0b00001111 101 & 15 = 0b00000101

両辺ともにビットが立っているところのみ1になります。

AND EAX, 3について考える

これはEAX &= 0b11;ということです
つまり、EAXを2進数表記にした際の最下位2bitのうち立っているビットのみを取り出すものです。

割り算との関係

10進数である数を100で割ったあまりを考える際、下2桁を取り出しますよね?
2進数でも同じです。
0b10=2で割ったあまりが欲しい場合は最下位ビットが立っているなら1で0なら0なんです。
0b100=4で割ったあまりが欲しいならば最下位2bitが4で割ったあまりになるのです。

以上が、CPU関連で必要な前提知識


%演算子

C言語における%演算子は割り算のあまりを計算する演算子です。
10÷3=3あまり1
なので
10%3=1
になります。
割り算というのはCPUの算術演算(普通の算数の演算)の中でも非常に遅くなるべく回避したいものなので
X%4を計算するときはbit毎の論理演算で代用するのが定石になります。



今回のプログラムについて

ビットマップのフォーマットによるとbmpに保存されている幅情報の単位はピクセルです。
1ピクセル=1ドット=点1個と置き換えてもいいです。

bmpには何色使えるかでいくつかの種類があります。
今回対象にしているフォーマットは推測ですが24bpp
1ピクセルごとに24bit=3byte使うことで16777216色使えるフォーマットだと思います。

1行ごとに最低限必要なバイト数は3*width byteですが
これを4byteの倍数になるように0を付け加えなければいけません。

仮にwidth=31ピクセルの場合を考えます。
31*3=93byte = 93=4*23 + 1byteなので3byte足して1行ごとに96byteになります。
93から余りの1がパディング長なのではなく、96-93の3byteがパディング長なのです。

どうでもいいこと

ピクセル配列に追加されるバイト列なので
ダミーデータというとピクセル単位で追加される事を個人的に想起します。

投稿2018/03/04 17:10

asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

carnage0216

2018/03/04 17:13

本当に本当にどうもありがとうございます。 早速今から基礎をおさらいします。 夜分遅い中本当にどうもありがとうございました。
guest

0

あの本書のCPUの構造がわからないので同じような結果になるかはわからないのですが。自分なりにコードを書いてみました。結果としては同じような結果になると思いますでしょうか?

mov eax, width  // eax = width;
mov edx, 3  // edx = 3;
mul edx   // edx:eax = eax * edx;
がもともとのコードです。

以下が私の書いたコードです。
mov eax, width
mov ebx ,3
mul eax ,ebx //←こう書いた場合は掛け算の結果はeaxに入るのでしょうか?//
mov ebx ,eax //eaxの内容をebxに入れます。//

あーめんどくせー 四の五の言わんとやってみろなんだが、
やり方知らんと手が出せんだろからC/C++内にアセンブリ書いて呼んでみたサンプル。

C++

1/* 2 Visual C++ 32bit(x86)コンパイラで試すべし 3 */ 4 5#include <iostream> 6#include <cstdint> 7 8uint32_t your_asm(uint32_t width) { 9 uint32_t result; 10 __asm { 11 mov eax, width 12 mov edx, 3 13 mul edx 14 mov result, eax 15 } 16 return result; 17} 18 19uint32_t my_asm(uint32_t width) { 20 uint32_t result; 21 __asm { 22 mov eax, width 23 mov ebx, 3 24/* mul eax, ebx マチガイ */ 25 mul ebx 26 mov ebx, eax 27 mov result, ebx 28 } 29 return result; 30} 31 32int main() { 33 using namespace std; 34 for ( uint32_t i = 0; i < 10; ++i ) { 35 cout << i << '\t' << your_asm(i) << '\t' << my_asm(i) << endl; 36 } 37} 38 39/* 実行結果 400 0 0 411 3 3 422 6 6 433 9 9 444 12 12 455 15 15 466 18 18 477 21 21 488 24 24 499 27 27 50*/

投稿2018/03/05 02:41

編集2018/03/05 02:46
episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

carnage0216

2018/03/05 11:57

どうもありがとうございます。
carnage0216

2018/03/05 11:59

早速、32bitの場合と64bitの場合で試させて頂きます。
episteme

2018/03/05 12:07

64では無理よ、__asm 使えないから。
carnage0216

2018/03/05 12:19

そうなんですか。 意外ですね。 64ビットで実行できるものは32ビットではできないのに。 32ビットで実行できるもので 64ビットで実行できないのは。
carnage0216

2018/03/05 12:20

アホみたいに思われますが、 gccの方でも実行してみます。
episteme

2018/03/05 12:29

> gccの方でも実行してみます。 「おんなじコードをgccに食わせたらエラーが出ますなぜですか」 なんつーアホな質問せんように。
carnage0216

2018/03/05 12:32

またしても調べて頂いてすいません。 あ、決して、調べずに待っていたわけではありません。 epistemeさんの方が情報を見つけるのが速すぎるというか、不甲斐ないです。
carnage0216

2018/03/05 12:33

さすが、そこまでアホなことは、、、多分しないです。
carnage0216

2018/03/05 12:33

ベーシックc++とgccは扱い方が異なると思うので。
episteme

2018/03/05 12:46

> epistemeさんの方が情報を見つけるのが速すぎるというか 経験は人並み以上にありますからね。 求めるものがどこにあるか、あらかたの見当がつくんです。 見つけるのが速いんじゃなくて、近づくのが速いんだと思う。
carnage0216

2018/03/06 06:55

そうなんですか。 どこかの企業に勤めているエンジニアの方なのでしょうか?
guest

0

前の方が説明されているので、詳しくはお話しませんが、このコードは、単に4で割った余りを計算してwMarginにセットしているだけと思いますので、単にwidthに対して4で割った余りをセットするだけでも良いと思います。

ちなみに、アセンブラではコーディングでは、mulやdivや条件ジャンプは遅いので、高速処理をする場合には、極力避けるべきです。私はコンパイラーのコード生成部を実装していたことがありますが,2倍や4倍する場合などでは、mulを使う場合には、シフト命令を使っていました。
その意味からいうと、このコーディングでは高速化するには変えたほうが良いと思います。4で割った余りなら、単に0x03でAndすれば、1つの命令で済みます。

投稿2018/03/03 13:28

diracpaul

総合スコア157

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

carnage0216

2018/03/03 13:44

回答ありがとうございます。 ッということは載せたアセンブラプログラミング以外のアセンブリ命令の組み合わせでも同じような処理を実現できるのでしょうか?  勉強のため本とは違う形で結果のしょつ力が同じようなアセンブリプログラムを作りたいと考えています。
episteme

2018/03/03 20:53 編集

そらできるわな、モノによるけど。 かけ算はたし算の繰り返しやろ? mod 4 は and 3 やろ?
carnage0216

2018/03/04 14:47 編集

あの本書のCPUの構造がわからないので同じような結果になるかはわからないのですが。自分なりにコードを書いてみました。結果としては同じような結果になると思いますでしょうか? mov eax, width  // eax = width; mov edx, 3  // edx = 3; mul edx   // edx:eax = eax * edx; がもともとのコードです。 以下が私の書いたコードです。 mov eax, width mov ebx ,3 mul eax ,ebx //←こう書いた場合は掛け算の結果はeaxに入るのでしょうか?// mov ebx ,eax //eaxの内容をebxに入れます。// どうかよろしくお願いいたします。
diracpaul

2018/03/08 14:28 編集

私自身は、定年過ぎのおっさんなので、インテル系の新しいCPUの詳しい動作はちょっとわかりません。また、マニュアルも捨ててしまっており確実にこれで大丈夫とは言い切れませんが、ご勘弁ください。 掛け算の場合には、例えば、16bit同士の掛け算だった場合には、結果としては32bit必要となります。 インテル系チップの場合は、乗算するレジスタが決まっている為、eaxレジスタとオペランドで指定したレジスタと掛け算をするように決まっている為、mul命令のオペランドにはeaxレジスタを指定しないものと思われます。 インテル系チップの乗算に関するページがありました。下記のURLをご覧ください。 http://wisdom.sakura.ne.jp/programming/asm/assembly11.html ちなみに、私は、APPLEIIに搭載されていた6502や、PC88のZ80,PC98のインテルの86までです。 アセンブラプログミングの醍醐味というと、それぞれのCPUの特徴を加味して高速処理を実現することにあります。例えば、6502では、レジスタが非常に少ない欠点はあるもの先頭の256バイトのメモリに対するアクセスが早くレジスタの代わりに利用するとか、インテル系のチップではMOVSコマンド(Z80ではLDIR)を使って文字列転送が高速できる事などです。今は、インテル系のチップが主流ですが、昔はたくさんの種類があり大変面白かったです。 なお、今回の元になったアセンブラプログラムで修正すべきと思う点は、 mov ebx,3 の部分です。なぜ3なのか、これはこれが1ピクセルのバイト数と思いますが、コメントでこの事をもっと明確に書いた方がよかったのではと思います。それであれば、意図にが明確になったのではと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問