Nastavljamo ovu seriju postova o tome kako stvoriti naš operativni sistem. Danas se nećemo fokusirati na jednu temu, već ćemo od sada definirati neke korisne funkcije. Prvo ćemo definirati 3 funkcije koje ispunjavaju funkciju memcpy, memeset 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;
}
Svi se oni samoprovode. Ove funkcije sam preuzeo iz male C biblioteke, implementacija je obično slična u svim operativnim sistemima. Sada ćemo napraviti 3 simulirane funkcije, ali manipulirati nizovima. Oni bi ispunili funkciju 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;
}
Sada ćemo krenuti s nekim prilično zanimljivim funkcijama. Pomoću ovih funkcija možemo čitati i pisati na hardverske portove. To se obično radi s ASM-om i odgovara (na x86) uputama in y napolje. Da biste lako pozvali ASM sa C, koristite uputstva asm, uz opasnost da za sobom povlači da nije prenosiv. Ovoj rečenici dodajemo i Volatilan tako da GCC ne pokušava optimizirati taj tekst. S druge strane, asm instrukcija ima neobičan način prihvaćanja parametara, ali mislim da se to bolje razumije ako se pogledaju primjeri.
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 do sada, post 3, danas nismo učinili ništa dopadljivo, ali definirali smo funkcije koje će nam dobro doći u budućnosti. Obavijestite 64-bitne korisnike da radim na popravljanju a buba što sprečava pravilno kompajliranje u 64 bita. U sljedećem postu vidjet ćemo važnu komponentu x86 arhitekture, GDT.
Da vidimo hoćete li komentirati kako riješiti grešku, jer nisam napredovao iz prvog dijela.
Mislim da su prokomentirali grešku u komentarima posta 2. Mislili su da je to nešto iz grube, ako se dobro sjećam
Kopirati bye to bye?
Bajt po bajt očito ..., 2 uzastopna neuspjeha ne čine uspjehom, potvrđeno.
Puno vam hvala na jako dobrom postu i pratim ga, imam nekoliko pitanja:
1. Kada kažete "Da biste lako nazvali ASM s C, koristi se asm uputa, s opasnošću da ona podrazumijeva da nije prenosiv", na što mislite kada kažete "s opasnošću da ona podrazumijeva da nije prenosiva '?
2. Ako bi se napravio „profesionalni“ operativni sistem (da tako kažem), ovaj dio pristupa hardveru bio bi izveden u Assembleru.
3. Kako bi se to radilo u Asembleru?
Zahvaljujući vama što ste ga pratili, odgovorit ću jedno po jedno na pitanja:
1- Pa, problem s asm instrukcijom je taj što ona ne postoji ni u jednom C standardu, pa je svaki prevodilac implementira na svoj način (ako je implementira). U ovom slučaju može se kompajlirati s GCC-om i Clang-om (u tom smislu sliči GCC-u), ali to nećete moći u drugim kompajlerima poput Intel C-a (ovaj koristi Intel Syntax u ASM-u).
2- Profesionalno, jedan i drugi dio trebaju biti jasno odvojeni arhitekturom i drugim zajedničkim dijelom. Nije potrebno to raditi u asembleru (Linux ga ima unutar C)
3- U asembleru je i to vrlo jednostavno, ali imate ga u zasebnoj datoteci. S druge strane, ako imamo argumente, prvo ih moramo proslijediti registrima i to može još malo zabrljati, a zatim se poziva outb ili inb i funkcija se proglašava globalno vidljivom. Zatim iz C morate napraviti zaglavlje koje deklarira "eksternu" funkciju. U NASM-u (Intel Syntax) to bi bilo otprilike ovako:
outb:
push ebp
mov ebp, esp
mov eax, [ebp + 12]
mov edx, [ebp + 8]
van dx, al
mov esp, ebp
pop ebp
odbacivanje
Pitanje, koju sintaksu koristite? Ne razumijem zašto toliko različitih asm xD MASM, FASM, NASM, AT & T ...
A ako imate vremena, možete li objasniti crtu:
asm volatile ("outb% 1,% 0" :: "dN" (port), "a" (vrijednost));
Sve dok "asm volatile" nisam razumio xD "outbyte 1,0?"
Ili je to 1 -> »dN» (port), 0 -> «a» (vrijednost)?
Ako je ovo drugo, ne razumijem što je "dn", a što "a" ...
Hvala vam puno na doprinosu! Nevjerovatno !!
Sintaksa koju koristim je AT&T, što GCC interno koristi, iako se to može učiniti bez problema u NASM-u (prilagođavanje sintakse). Što se tiče te složene izjave asm volatile, mogu vam samo reći da je to slučaj zbog OUU-a, jer koristi neke parametre da bi znao kako treba prosljeđivati podatke. Na primjer, "a" je poseban x86 operand koji se koristi za predstavljanje registra. Cijela lista je ovdje: http://gcc.gnu.org/onlinedocs/gcc/Constraints.html#Constraints
Pa, stvarno mi treba pomoć, nov sam u tome i nemam ni najmanju ideju šta da radim sa onim što je napisano u terminalu, može li mi netko pomoći?