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

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

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

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

Q&A

解決済

2回答

973閲覧

アセンブリ言語 セクタ読み出し関数について

kazuyakazuya

総合スコア193

アセンブリ言語

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

0グッド

0クリップ

投稿2020/01/20 10:25

編集2020/01/20 10:43

参考書でセクタを読み出す関数を定義していたのですがそれに関してわからないところがあるのでお願いします。
イメージ説明
イメージ説明
イメージ説明
イメージ説明
イメージ説明
作って理解するOS

s

1read_chs: 2 3push bp 4mov bp,sp 5push 3 6push 0 7 8push bx 9push cx 10push dx 11push es 12push si 13 14mov si,[bp + 4] 15mov ch,[si + drive.cyln + 0] 16mov cl,[si + drive.cyln + 1] 17shl cl,6 18or cl,[si + drive.sect] 19 20mov dh,[si + drive.head] 21mov dl,[si + 0] 22mov ax,0x0000 23mov es,ax 24mov bx,[bp + 8] 25 26.10L: 27mov ah,0x02 28mov al,[bp + 6] 29 30int 0x13 31jnc .11E 32 33mov al,0 34jne .10E 35.11E: 36 37cmp al,0 38jne .10E 39 40mov ax,0 41dec word[bp - 2] 42jnz .10L 43 44.10E 45mov ah,0 46 47pop si 48pop es 49pop dx 50pop cx 51pop bx 52 53mov sp,bp 54pop bp 55 56ret 57

mov si,[bp + 4] ;mov bp,sp
mov ch,[si + drive.cyln +0]
mov cl,[si + drive.syln + 0]

ここのくだりが理解できません。

各レジスタにはソフトウエア割り込みを使って読み込みのために適切な情報を
入れますが
その値の入り方がよくわかりません。

このときスタックに積まれているのは

9 si 1バイト push si SP----------------- 8 es 1バイト push es 7 dx 1バイト push dx 6 cx 1バイト push cx 5 bx 1バイト push bx 4 0 1バイト push 0 3 3 1バイト push 3 2 bp 1バイト push bp 1 リターンアドレス 4バイト(プロテクトモードでもそのままアドレスが配置される?)

mov si,[bp + 4] ;によって
siレジスタには6のcxレジスタに入っている値が入るはずですよね?
仮にアドレスだと仮定して考えると・・・
mov ch,[si + drive.cyln +0]
drive.syln は 2バイト(?)だから
4の0になるってことですか?

スタックとこのレジスタに値を入れるため命令らの関係がよく分からないので教えてください。

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

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

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

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

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

dodox86

2020/01/20 10:37 編集

ご質問の主旨とは違いますが、そもそもコードの書き写しに間違いがないでしょうか。アセンブルが通りませんね、きっと。 > mov si,[bp + 4] ;mov bp,sp > mov ch,[si + drive.cyln +0] > mov cl,[si + drive.syln + 0] は、以下が正しいと思います。(文献を確認してください) mov si,[bp + 4] mov ch,[si + drive.cyln + 0] mov cl,[si + drive.cyln + 1] そうでないと、スペルミスは置いておいても、CHとCLレジスタに同じ値が入ってしまいます。(意味不明です)
kazuyakazuya

2020/01/20 10:44

ご指摘の通りミスがあったので訂正しました。
guest

回答2

0

このときスタックに積まれているのは...
9 si 1バイト push si SP-----------------
8 es 1バイト push es
7 dx 1バイト push dx
...

の時点で残念ながら間違っています。read_chs.s のソース(画像)の冒頭に「スタックフレームの構築」と称してコメントが記述されています。これをよく見てください。また、各命令について理解が間違っているところがあるようです。以下2つを念頭に置いてください。
push命令はオペランド(この場合はBPやBXなどの16ビットレジスタ)の幅でスタックに値をpushする。AHやALなど、1バイト(8ビット)単位ではpushできない。
・SPは、今現在のスタックの最新(トップ)の位置を示している。

コードの一部を順を追って見ていきます。プロシージャread_chsが呼ばれた時点でまず、スタックフレームは以下のようになっています。SPは、IP戻り番地を示しています。

SPから見たオフセット内容
+0IP戻り番地
+2パラメータバッファ
+4セクタ数
+6コピー先

ここで push bp, mov bp, sp を実行すると、

SPから見たオフセット内容
+0BP
+2IP戻り番地
+4パラメータバッファ
+6セクタ数
+8コピー先

となります。BPにはSPの値が入るので、ここでスタックフレームが構築されています。以降、BPは常にBPのオリジナルの値が収められたアドレスを指します。

続けて

asm

1push 3 2push 0 3push bx 4push cx 5push dx 6push es 7push si

を実行すると、以下のようになります。ちなみに03の直値をpushすると、16ビットで収められます。

SPから見たオフセット内容
+0SI
+2ES
+4DX
+6CX
+8BX
+10(0Ah)0
+12(0Ch)3
+14(0Eh)BP
+16(10h)IP戻り番地
+18(12h)パラメータバッファ
+20(14h)セクタ数
+22(16h)コピー先

mov si,[bp + 4] を実行すると、今、BPが指し示しているアドレス+4 の値、つまりはパラメータバッファのアドレスをSIにロードするので、以降、SIをベースとして正しく参照できるようになる、と言うわけです。

投稿2020/01/20 12:29

編集2020/01/21 10:18
dodox86

総合スコア9254

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

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

kazuyakazuya

2020/01/20 13:11 編集

回答ありがとうございます。 siレジスタらは16bitでしたね。すみません
kazuyakazuya

2020/01/20 13:44

また勘違いなのか理解できないのでお願いします。 mov ch,[si + drive.cyln +0] これでシリンダ番号を求めることができるみたいですが si・・・パラメーターバッファ drive.cyln・・・resw2バイトより、2*2=4 mov ch,[si + 4 +0] つまり、この考えが正しければchレジスタには コピー先のデータが積まれているアドレスにアクセスすることに なると思うのですが これは間違えていますよね?
dodox86

2020/01/20 14:05

> mov ch,[si + 4 +0] > つまり、この考えが正しければchレジスタには > コピー先のデータが積まれているアドレスにアクセスすることに いえ、残念ですが、間違っています。 driveの(疑似命令)構造体は以下ですが、 resw 1はWORD(16ビット)でそれぞれ1個ずつ割り当てる算段です。 .no resw 1 .cyln resw 1 .head resw 1 .sect resw 1 .noで2バイトを割り当て、.cylnはその次なので、 mov ch, [si + drive.cyln + 0]は mov ch, [si + 2 + 0] と置き換えます。 ですので、正しくシリンダーのデータ部分にアクセスできています。 ※構造体メンバーのデータ項目の「サイズ」と「アドレス(オフセット)」を混同しないよう気を付けましょう。
kazuya0409

2020/01/21 00:33

わけあってログインできなかったので 別垢から質問させていただきます。 >確かにオフセットとサイズは別物でした・・・。 mov ch, [si + 2 + 0] は「セクタ数」を指すことになると思いますが そこは合っていますか?
dodox86

2020/01/21 02:52 編集

> mov ch, [si + 2 + 0] > は「セクタ数」を指すことになると思いますが ??なぜ。間違っています。 > mov ch, [si + 2 + 0] と置き換えます。 > ですので、正しくシリンダーのデータ部分にアクセスできています。 と私が先に書いているのに、なぜそうなっちゃうのか。セクタ番号を取り出している部分は以下だと思います or cl, [si + drive.sect] これは、or cl, [si + 6]と読み替えられます。 read_chsプロシージャに渡された「読み出すセクタ数」を取り出している部分は、 ... mov ah,0x02 mov al,[bp + 6] int 0x13 ... の mov al, [bp + 6]の部分です。 ※CLへは、読み出す「先頭のセクタ番号」です。ALは、読み出すセクタ数です。INT 13HのディスクBIOSアクセスに繋がります。 > わけあってログインできなかったので > 別垢から質問させていただきます。 なぜ、みすみす規約違反をするのでしょう。(https://teratail.com/legal) 私自身は運営の者ではありませんし厳しいことは書きたくないのですが、控えていただきたいものです。
kazuyakazuya

2020/01/21 03:49

すみません。アカウントは消しておきます。 mov ch, [si + 2 + 0] この時点で +12 パラメータバッファ のアドレスを示している。 よって、si + 2 + 0 は、プラス14を指すのでは?と勝手に考えていたのですが そういうことではないのですか?
dodox86

2020/01/21 04:46 編集

> よって、si + 2 + 0 > は、プラス14を指すのでは?と勝手に考えていたのですが なるほど、レジスタの動作を誤解されているのでしょうか。 例えば今、SIレジスタの中身が1000だとします。mov ch, [si + 2 ] はSIレジスタの中身+2の結果をアドレスとして使い(間接アドレッシング)、つまりは1002番地を先頭として1バイト(CHレジスタは8ビットだから)メモリから取り出し、CHレジスタにロードします。このとき、SIレジスタの中身が変わる訳ではありません。その後でmov ch, [si + 2]としようが、mov ah, [si + 100] としようが、SIレジスタの値はずっと変わりません。あくまでSIレジスタの中身の値をベースとして+2 や+100した結果をアドレスとしてアクセスします。 頭でばかり考えず(頭でまず考えることも大事ですが)、実際にデバッガーを使って動作させてみましょう。
kazuyakazuya

2020/01/21 07:49 編集

あ、すみません 自分で質問しておいてあれですが文がおかしかったです(質問の趣旨など) mov ch, [si + 2 + 0] この時点でsiレジスタには +12 パラメータバッファのアドレスが入っている。 (mov si,[bp + 4] この命令によりsiレジスタにアドレスがセットされた) それを踏まえたうえで mov ch, [si + 2 + 0] この命令を見ると si + 2 + 0 ↓ 12 + 2 + 0 ↓ 14 オフセット14のアドレスを指すことになるのでは?と思いました。 >実際にデバッガーを使って動作させてみましょう。 こういうときのためのデバッガーですし、使ってみます。
dodox86

2020/01/21 08:03

書籍に記載されているオリジナルのコードと動作を順を追って確認してください。 mov si, [bp + 4] の前、そもそものread_chsの冒頭で、push bp; mov bp, sp となっていて、このときにBPがセットされています。そのあといくらpushしようが、BPの値は変わりません。ですので、mov si, [bp +4] でパラメータバッファの先頭アドレスがSIにセットされますし、mov ch, [si + 2] は、パラメータバッファの先頭から"+12ではなく"、+2を示します。(回答どおりです)
kazuyakazuya

2020/01/21 08:44

そのオフセットというのは dodox86さんが回答で書いていただいた表を照らし合わせてのものです。 それを前提に バッファパラメーター + 2 はオフセット14になるという意味で書きました。
dodox86

2020/01/21 10:25 編集

> バッファパラメーター + 2 > はオフセット14になるという意味で書きました。 いまだに認識に相違がある気もしますが、表中のオフセット値は16進数で書いていました。分かりづらい面もあったので誤解がないよう、10進数に統一して修正、注記しました。 尚、回答の表はあくまで「SPから見たオフセットと、スタック中に格納されている値」を示しています。その認識が合っていればOKです。
guest

0

ベストアンサー

si:変数の先頭アドレス
drive.cyln:構造体のシリンダ部のオフセット

ということで、
si+drive.cyln でシリンダ部のアドレス、となりまして、
mov ch,[si + drive.cyln + 0]
で、シリンダ下位バイトをchに、
mov cl,[si + drive.cyln + 1]
で、シリンダ上位バイトをclに格納しているってことですな

push bp
mov bp,sp
まず、bpをpush(たんにbpの値を退避してるだけ)
で、その時のspの値をbpにいれてます

mov si,[bp + 4]
その関数の引数のアドレスを求めてます

まずは、関数呼び出し時点のスタックの構成を考えてみよう
#呼び出し側がどういう命令列で呼び出してるのかを読む必要あり


スタックの状態を図示します
※1行2バイトね

・関数のcall時
[引数アドレス]→SP

・関数の入り口
[戻りアドレス]→SP
[引数アドレス]

・push BP
[push BP]→SP
[戻りアドレス]
[引数アドレス]

・mov bp,sp
[push BP]→SP=BP
[戻りアドレス]
[引数アドレス]

・そのあとなんやかやで、、
[push si]→SP
[push es]
[push dx]
[push cx]
[push bx]
[push 0]
[push 3]
[push BP]→BP
[戻りアドレス]
[引数アドレス]

・mov si,[bp+4]
[push si]→SP
[push es]
[push dx]
[push cx]
[push bx]
[push 0]
[push 3]
[push BP]→BP
[戻りアドレス]
[引数アドレス]→(siに引数アドレス) BP+4

というふーになります。

投稿2020/01/20 10:55

編集2020/01/21 11:37
y_waiwai

総合スコア88024

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

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

kazuyakazuya

2020/01/20 13:17

ありがとうございます。
y_waiwai

2020/01/21 08:03

> mov ch,[si + drive.cyln + 0] ch、clというのはバイトですぜ。 当然、[si + drive.cyln + 0]は、バイト単位の計算になります。
kazuyakazuya

2020/01/21 08:53

なんか今回も妙な勘違いをしているような・・・ chはcxの上位 clはcxの下位 そういった意味で mov ch,[si + drive.cyln + 0] mov ch,[si + drive.cyln + 1] また、pushしても値が変化しないというのも理解しているつもりです。 ただ、・・・ [si + drive.cyln + 0] これが指しているアドレス(スタック)は ”セクタ番号”先頭アドレス を指してしまうような・・・
y_waiwai

2020/01/21 09:01

drive.cylnだから、シリンダ番号のデータじゃないの? んで、実際のセクタ読み出しはLinuxのカーネルかBIOSに対しての何らかのシステムコールになると思われますが、その呼出時のレジスタ内容をどうするのかというのも抑えときましょう #たぶん、chに下位、clに上位入れてシフトさせて、というのはそこらへんを踏まえてると思われます #イマサラBIOSの資料漁るのもカンベンw
kazuyakazuya

2020/01/21 10:41 編集

>その呼出時のレジスタ内容をどうするのかというのも抑えときましょう わかりました。 chレジスタにはシリンダ番号が格納されている先頭アドレスの下位を入れる・・・と書かれていますが まずこれを見てみます。(chにはシリンダ番号があるアドレスが入るみたいだが) mov ch,[si + drive.cyln + 0] この命令が下されるとchレジスタは dodox86さんが提示した表で見てみると パラメーターバッファのアドレスを指しているはず。 つまり、これは↓ si + drive.cyln + 0 パラメーターバッファがあるアドレス + 2(ですよね?) + 0 つまり、chレジスタに入るアドレスというのは シリンダ番号ではなく パラメーターバッファより2バイトあとに設置されている セクタ番号を指すことになるのでは?と考えている・・・という感じなのですが どっから勘違いしているのでしょうか?
y_waiwai

2020/01/21 10:21

drive構造体は +0 drive番号 +2 シリンダ番号 +4 ヘッド番号 +6 セクタ番号 になってるでしょ
dodox86

2020/01/21 10:36

あれ、終わったと思っていたのですが、y_waiwaiさんの方でやり取りが続いてたのですね。(今気づいた) どうも堂々巡りしているようです。 > つまり、siレジスタに入るアドレスというのは > シリンダ番号ではなく > パラメーターバッファより2バイトあとに設置されている > セクタ番号を指すこと 言葉の使い方だけの間違いかもしれませんが、SIレジスタの値は変わりません。その時点で認識が間違っている気がします。
kazuyakazuya

2020/01/21 10:40

あ、違った si レジスタではなくchレジスタの書き間違いです。
y_waiwai

2020/01/21 10:41

まあ、真摯に学ぼうとしているようなので出来る限り相手したいところではありますがw
dodox86

2020/01/21 10:44

同意します。>y_waiwaiさん
y_waiwai

2020/01/21 10:45

[si + drive.cyln + 0] si+drive.cylnはシリンダ番号のアドレスってのはわかってるでしょ で、[]で囲ってるのは、そのアドレスのデータを示します なので、ch、clにはシリンダデータが入ります
kazuyakazuya

2020/01/21 10:54

si+drive.cyln が シリンダ番号のアドレス になる理由がわからないです。 si + drive.syln = シリンダ番号があるアドレス si = パラメーターバッファが置かれているアドレス(スタック上の) drive.cyln = 2 -------si+drive.cyln------- [パラメーターバッファが置かれているアドレス(スタック上の] + [2] この2つを合計したアドレスがシリンダ番号のアドレスになるという話だと思うのですが パラメーターバッファが置かれているアドレス(スタック上の ↑これが置かれているアドレス +2 の場所には ====== +18(12h) パラメータバッファ +20(14h) セクタ数 +22(16h) コピー先 ====== セクタ数のデータがあるはずですが それがなぜシリンダ番号になるのかが理解できないです。
y_waiwai

2020/01/21 11:37

回答に追記。 これでわかるかな。。。(ぜえぜえ
kazuyakazuya

2020/01/21 11:52 編集

ありがとうございます。 あ、どこで勘違いが発生したかわかったぞ! [引数(アドレス)] ↓(以下のように積まれている) [ドライブ] 0 [シリンダ] 2← [ヘッド] 4 [セクタ] 6 +2 でシリンダを指すんですね。 となると、 dodox86さんに書いていただいた表の +18(12h) パラメータバッファ +20(14h) セクタ数 +22(16h) コピー先 1:パラメーターバッファ 2:セクタ数 3:コピー先 ですが、実際は 1:ドライブ 2:シリンダ 3:ヘッド 4:セクタ の順番のはずですが となると、パラメーターバッファの正体はなんですか?
y_waiwai

2020/01/21 11:56

もちっと詳しく書くと、関数のcall時にはこうなってます ・関数のcall時 [引数アドレス、(+0)のアドレス]→SP [引数データ](+0) [引数データ](+2) [引数データ](+4) [引数データ](+6) 引数のパラメータバッファもスタックにpushされた状態で関数が呼ばれます とうぜん、呼んだほうは関数終了してから、スタックを引数+引数アドレスの分戻します
kazuyakazuya

2020/01/21 12:44

ありがとうございます。 read_chs引数全体 と drive構造体 を勝手にごっちゃごっちゃに考えてしまってでの勘違いでした。すみません こっちのコメント欄で解決したのでy_waiwaiさんをベストアンサーにさせていただきます。 ありがとうございました。
kazuyakazuya

2020/01/21 12:45

あれ、ベストアンサーもう選んでいましたね。w (押してなかったはずだが・・・)
y_waiwai

2020/01/21 12:45

いやいや、せんでいいよw
dodox86

2020/01/21 15:17

BAは、変えられるならそうしてください。>kazuyakazuyaさん
kazuyakazuya

2020/01/24 07:28

1つよろしいでしょうか? (また全く同じ内容の質問を立てるのもあれなので・・・) CXレジスタには [シリンダ10bit:セクタ番号6bit] といった感じで入るようになっていると思いますが・・・ 6bitということは 64個しか表現できませんよね? もし、6bit以上に指定した場合 or演算のところで値がおかしくなりエラーになると思いますが セクタってふつう、だいたい255個くらいありますよね? 64個で足りるものなのですか?
y_waiwai

2020/01/24 07:42

http://park12.wakwak.com/~eslab/pcmemo/hdisk/index.html まずはHDDの構造の勉強しましょう セクタというのはトラックあたりのデータブロックの数です で、これを踏まえ、 http://archive.linux.or.jp/JF/JFdocs/hdd-intro.html > ただし、もともとの BIOS の仕様では シリンダー番号指定に 10bit、ヘッド番号指定に 4bit、セクター番号指定に 1~63 を使うようになっており、 というふーに、BIOSでは6ビット分しか指定がなかったのでした。 まあ、イマドキでは、CHSの指定というのは仮想的になってるので、意味を持ちません。 あくまで、昔の仕様です
kazuyakazuya

2020/01/24 07:48

わかりました。ありがとうございます
kazuyakazuya

2020/02/01 06:07

drive構造体に関して、 ドライブ 1 0x0001 シリンダ 1 0x0001 ヘッド 1 0x0001 セクタ 1 0x0001 と定義されていますが リトルエンディアンとビッグエンディアンで メモリに配置される 0x0001のバイト順は変わりますか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問