Emulowanie Linusa Torvaldsa: Stwórz swój własny system operacyjny od podstaw (III)

Kontynuujemy tę serię postów o tym, jak stworzyć nasz system operacyjny. Dzisiaj nie będziemy skupiać się na jednym temacie, ale od teraz zdefiniujemy kilka przydatnych funkcji. Przede wszystkim zdefiniujemy 3 funkcje, które spełniają funkcję memcpy, zestaw memów y memcmmp:

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;
}

Wszystkie realizują się samodzielnie. Wziąłem te funkcje z małej biblioteki C, implementacja jest zwykle podobna we wszystkich systemach operacyjnych. Teraz zrobimy 3 podobne funkcje, ale do manipulowania łańcuchami. Pełniłyby funkcję tzw strcpy, strcat 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;
}

Zajmiemy się teraz kilkoma całkiem ciekawymi funkcjami. Dzięki tym funkcjom możemy odczytywać i zapisywać dane na portach sprzętowych. Zwykle odbywa się to za pomocą ASM i odpowiada (na x86) instrukcjom in y na zewnątrz. Aby łatwo wywołać ASM z C, użyj instrukcji asm, z niebezpieczeństwem, że nie jest przenośny. Do tego zdania dodajemy lotny więc GCC nie próbuje zoptymalizować tego tekstu. Z drugiej strony instrukcja asm ma ciekawy sposób przyjmowania parametrów, ale wydaje mi się, że można to lepiej zrozumieć, patrząc na przykłady.

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;
}

I oto post 3, dzisiaj nie zrobiliśmy nic błyskotliwego, ale zdefiniowaliśmy kilka funkcji, które przydadzą się na przyszłość. Uwaga dla użytkowników 64-bitowych, że pracuję nad naprawieniem pliku pluskwa co uniemożliwia poprawną kompilację w wersji 64-bitowej. W następnym poście zobaczymy ważny komponent architektury x86, GDT.


9 komentarzy, zostaw swoje

Zostaw swój komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

*

*

  1. Odpowiedzialny za dane: Miguel Ángel Gatón
  2. Cel danych: kontrola spamu, zarządzanie komentarzami.
  3. Legitymacja: Twoja zgoda
  4. Przekazywanie danych: Dane nie będą przekazywane stronom trzecim, z wyjątkiem obowiązku prawnego.
  5. Przechowywanie danych: baza danych hostowana przez Occentus Networks (UE)
  6. Prawa: w dowolnym momencie możesz ograniczyć, odzyskać i usunąć swoje dane.

  1.   nazwa powiedział

    Zobaczmy, czy skomentujesz, jak rozwiązać błąd, ponieważ nie posunąłem się naprzód od pierwszej części.

    1.    O_Pixote_O powiedział

      Myślę, że skomentowali błąd w komentarzach do postu 2. Myśleli, że to coś z gruba, jeśli dobrze pamiętam

  2.   Niebieska czaszka powiedział

    Skopiuj do widzenia?

    1.    Niebieska czaszka powiedział

      Oczywiście bajt po bajcie... 2 kolejne chybienia nie dają trafienia, potwierdzone.

  3.   ruby232 powiedział

    Dziękuję bardzo za bardzo dobry post i śledzę go, mam kilka pytań:
    1. Kiedy mówisz „Aby łatwo wywołać ASM z C, używana jest instrukcja asm, z niebezpieczeństwem, że nie jest przenośna”, co masz na myśli, mówiąc „z niebezpieczeństwem, że nie jest przenośna”?

    2. Jeśli zamierzasz stworzyć „profesjonalny” system operacyjny (że tak powiem), ta część dostępu do sprzętu byłaby wykonywana w asemblerze.

    3. Jak by to było zrobione w asemblerze?

    1.    AdrianArroyoStreet powiedział

      Dzięki za śledzenie go, odpowiem na pytania jeden po drugim:
      1- Cóż, problem z instrukcją asm polega na tym, że nie istnieje ona w żadnym standardzie C, więc każdy kompilator implementuje ją na swój własny sposób (jeśli w ogóle ją implementuje). W tym przypadku można go skompilować za pomocą GCC i Clang (pod tym względem wygląda bardzo podobnie do GCC), ale nie będzie można tego zrobić za pomocą innych kompilatorów, takich jak Intel C (używa składni Intel w ASM).
      2- Profesjonalnie jedna część powinna być wyraźnie oddzielona od drugiej architekturą i inną częścią wspólną. Nie musisz tego robić w asemblerze (Linux ma to w C)
      3- W asemblerze jest to również bardzo proste, ale masz to w osobnym pliku. Z drugiej strony, mając argumenty, musimy najpierw przekazać je do rejestrów, a to może trochę namieszać, a następnie wywołuje się outb lub inb i deklaruje funkcję jako globalnie widoczną. Następnie z C będziesz musiał utworzyć nagłówek, który deklaruje funkcję „extern”. W NASM (Intel Syntax) wyglądałoby to mniej więcej tak:
      wyjście:
      pchnij ebp
      mov ebp, zwł

      ruch eax, [ebp + 12]
      ruch edx, [ebp + 8]

      out dx, al

      ruch esp, ebp
      pop ebp
      gnić

  4.   Max powiedział

    Pytanie, jakiej składni asm używasz? Nie rozumiem po co tyle różnych asm xD MASM, FASM, NASM, AT&T…
    A jeśli masz czas, czy mógłbyś wyjaśnić linię:
    asm volatile("outb %1, %0" :: "dN"(port), "a"(wartość));

    Nawet "asm volatile" zrozumiałem xD "outbyte 1,0?"
    A może 1–>»dN”(port), 0–>„a”(wartość)?
    Jeśli to drugie, nie rozumiem, co to jest „dn”, a co to jest „a”…

    Dziękuję bardzo za wkład! Iniemamocni!!

    1.    AdrianArroyoStreet powiedział

      Składnia, której używam, to AT&T, która jest używana wewnętrznie przez GCC, chociaż można to zrobić bez problemów w NASM (dostosowując składnię). Jeśli chodzi o to złożone stwierdzenie asm volatile, mogę ci tylko powiedzieć, że dzieje się tak z powodu GCC, ponieważ używa on pewnych parametrów, aby wiedzieć, w jaki sposób dane powinny być przekazywane. Na przykład „a” to specjalny operand dla x86, który jest używany do reprezentowania rejestru a. Cała lista jest tutaj: http://gcc.gnu.org/onlinedocs/gcc/Constraints.html#Constraints

  5.   charizardfire52 powiedział

    Cóż, naprawdę potrzebuję pomocy, jestem w tym nowy i nie mam pojęcia, co zrobić z tym, co jest napisane w terminalu, czy ktoś może mi pomóc?