オペレーティングシステムの作成方法に関するこの一連の投稿を続けます。 今日は3つのトピックに焦点を当てるつもりはありませんが、これからいくつかの便利な関数を定義します。 まず、次の機能を満たすXNUMXつの関数を定義します。 memcpy, メモリセット y memcmp:
void* ND::Memory::Set(void* buf, int c, size_t len)
{
unsigned char* tmp=(unsigned char*)buf;
while(len--)
{
*tmp++=c;
}
return buf;
}
void* ND::Memory::Copy(void* dest,const void* src, size_t len)
{
const unsigned char* sp=(const unsigned char*)src;
unsigned char* dp=(unsigned char*)dest;
for(;len!=0;len--) *dp++=*sp++;
return dest;
}
int ND::Memory::Compare(const void* p1, const void* p2, size_t len)
{
const char* a=(const char*)p1;
const char* b=(const char*)p2;
size_t i=0;
for(;i<len;i++)
{
if(a[i] < b[i]) return -1; else if(a[i] > b[i])
return 1;
}
return 0;
}
それらはすべて自己実装型です。 私が小さなCライブラリから取得したこれらの関数は、通常、すべてのオペレーティングシステムで実装が類似しています。 次に、3つのシミュレートされた関数を実行しますが、文字列を操作します。 彼らはの機能を果たします strcpy, ストラキャット y strcmp.
size_t ND::String::Length(const char* src)
{
size_t i=0;
while(*src--)
i++;
return i;
}
int ND::String::Copy(char* dest, const char* src)
{
int n = 0;
while (*src)
{
*dest++ = *src++;
n++;
}
*dest = '';
return n;
}
int ND::String::Compare(const char *p1, const char *p2)
{
int i = 0;
int failed = 0;
while(p1[i] != '' && p2[i] != '')
{
if(p1[i] != p2[i])
{
failed = 1;
break;
}
i++;
}
if( (p1[i] == '' && p2[i] != '') || (p1[i] != '' && p2[i] == '') )
failed = 1;
return failed;
}
char *ND::String::Concatenate(char *dest, const char *src)
{
int di = ND::String::Length(dest);
int si = 0;
while (src[si])
dest[di++] = src[si++];
dest[di] = '';
return dest;
}
私たちは今、いくつかの非常に興味深い機能を使っています。 これらの関数を使用して、ハードウェアポートの読み取りと書き込みを行うことができます。 これは通常ASMで実行され、(x86では)命令に対応します in y でる。 CからASMを簡単に呼び出すには、命令を使用します ASM、持ち運びできないという危険性があります。 この文に、 GCCがそのテキストを最適化しようとしないようにします。 一方、asm命令にはパラメータを受け入れる奇妙な方法がありますが、例を見ると理解しやすいと思います。
void ND::Ports::OutputB(uint16_t port, uint8_t value)
{
asm volatile("outb %1, %0" : : "dN"(port), "a"(value));
}
uint8_t ND::Ports::InputB(uint16_t _port)
{
unsigned char rv;
asm volatile("inb %1, %0" : "=a"(rv) : "dN"(_port));
return rv;
}
そして、これまでのところ3の投稿では、今日は派手なことは何もしていませんが、将来に役立つ関数を定義しました。 私が修正に取り組んでいる64ビットユーザーへの通知 バグ これにより、64ビットで正しくコンパイルできなくなります。 次の投稿では、x86アーキテクチャの重要なコンポーネントであるGDTについて説明します。
私は最初の部分から進んでいなかったので、バグを解決する方法についてコメントするかどうか見てみましょう。
彼らは投稿2のコメントのエラーについてコメントしたと思います。私が正しく覚えていれば、彼らはそれが地味なものだと思っていました。
さようならをさようならにコピーしますか?
バイトごとに明らかに...、2つの連続した失敗は成功しません、確認されました。
非常に良い投稿をありがとうございました、そして私はそれをフォローしています、私はいくつかの質問があります:
1.「CからASMを簡単に呼び出すには、asm命令を使用しますが、移植できないという危険があります」と言うときは、「それが伴う危険がある」とはどういう意味ですか。ポータブルではありません」?
2.「プロフェッショナル」オペレーティングシステムが(いわば)作成された場合、ハードウェアへのアクセスのこの部分はアセンブラーで実行されます。
3.アセンブラーでどのように実行されますか?
フォローしていただきありがとうございます。質問にXNUMXつずつお答えします。
1-まあ、asm命令の問題は、どのC標準にも存在しないため、各コンパイラが独自の方法で実装することです(実装する場合)。 この場合、GCCとClang(この点ではGCCによく似ています)でコンパイルできますが、Intel C(これはASMでIntel構文を使用します)のような他のコンパイラーではコンパイルできません。
2-専門的には、ある部分と別の部分は、アーキテクチャと別の共通部分によって明確に分離する必要があります。 アセンブラで行う必要はありません(LinuxではC内にあります)
3-アセンブラでも非常に簡単ですが、別のファイルにあります。 一方、引数がある場合は、最初にそれをレジスタに渡す必要があります。これにより、もう少し混乱する可能性があります。次に、outbまたはinbが呼び出され、関数がグローバルに表示されるように宣言されます。 次に、Cから、「extern」関数を宣言するヘッダーを作成する必要があります。 NASM(Intel Syntax)では、次のようになります。
outb:
ebpをプッシュ
mov ebp、esp
mov eax、[ebp + 12]
mov edx、[ebp + 8]
out dx al
mov esp、ebp
ポップebp
RET
質問、どの構文を使用しますか? なぜこれほど多くの異なるasmxD MASM、FASM、NASM、AT&T ..が必要なのかわかりません。
そして、時間があれば、次の行を説明していただけますか。
asm volatile( "outb%1、%0" :: "dN"(ポート)、 "a"(値));
「asmvolatile」まで、xDの「outbyte1,0」を理解していましたか?
それとも1->»dN»(ポート)、0->«a»(値)ですか?
後者だと「dn」って何なのかわからない…
貢献していただきありがとうございます! 信じられない!
私が使用している構文はAT&Tです。これは、GCCが内部で使用しているものですが、NASMでは問題なく実行できます(構文の適応)。 asm volatileの複雑なステートメントに関しては、データの受け渡し方法を知るためにいくつかのパラメーターを使用しているため、GCCが原因であるとしか言えません。 たとえば、「a」は、レジスタを表すために使用される特別なx86オペランドです。 リスト全体はここにあります: http://gcc.gnu.org/onlinedocs/gcc/Constraints.html#Constraints
まあ、私は本当に助けが必要です、私はこれに不慣れで、ターミナルに書かれていることをどうするかについて少しもわかりません、誰かが私を助けることができますか?