x86.h static inline void outsl(int port, const void *addr, int cnt)

トップページ
jupiteroak.hatenablog.com


x86.h
https://github.com/mit-pdos/xv6-public/blob/master/x86.h#L33

static inline void outsl(int port, const void *addr, int cnt)
{
  asm volatile("cld; rep outsl"
               : "=S" (addr), "=c" (cnt)
               : "d" (port), "0" (addr), "1" (cnt)
               : "cc");
}

outsl関数は、先頭アドレスが引数addr・サイズが引数cnt×4のメモリ領域に格納されているデータを、引数port(I/Oポートアドレス)で指定されるI/O装置に書き込みます。

引数 int port
I/O装置を指定するI/Oポートアドレスです。

引数 void *addr
I/O装置に書き込みたいデータが格納されているメモリ領域の先頭アドレスです。

引数 int cnt
I/O装置に書き込みたいデータが格納されているメモリ領域のサイズ(バイト単位)÷4の値です。


処理の内容

インラインアセンブラ

asm volatile("cld; rep outsl"
               : "=S" (addr), "=c" (cnt)
               : "d" (port), "0" (addr), "1" (cnt)
               : "cc");

関数内のインラインアセンブラを解釈すると以下のようになります。

アセンブリ言語命令

cld
rep outsl

インラインアセンブラの命令を実行する前の状態

・edxレジスタ(入力レジスタのリストにdで表記されている)
引数portの値が設定されています。
・esiレジスタ(入力レジスタのリストに0、出力レジスタのリストにSで表記されている)
引数addrの値が設定されています。
・ecxレジスタ(入力レジスタのリストに1、出力レジスタのリストにcで表記されている)
引数cntの値が設定されています。

cld

cld命令は、EFLGSレジスタのDFフラグ(bit10)をクリアします。DFフラグが0の時にストリング命令(movs、lods、cmps、outslなど)を実行すると、esiレジスタの値がインクリメントするようになります。

rep outsl

repプレフィックス(リピートプレフィックス)を付けられた命令は、ecxレジスタに設定された値の回数分繰り返すように実行されます。
outsl命令は、esiレジスタの値で指定されたメモリに格納されている4バイトのデータを、edxレジスタの値(I/Oポートアドレス)で指定されるI/O装置に書き込みます。
DFフラグが0の時(前のcld命令によってDFフラグが0の値になっている)、outsl命令を実行するとesiレジスタの値が4インクリメントされます。
repプレフィックスの効果により、この操作をecxレジスタに設定された値の回数分繰り返します。

参考
http://exlight.net/devel/8086/string.html
https://mudongliang.github.io/x86/html/file_module_x86_id_223.html