x86 simulator - geheugenmodel, welke aanpak?

Pagina: 1
Acties:
  • 107 views sinds 30-01-2008
  • Reageer

  • metaal
  • Registratie: Juli 2001
  • Laatst online: 31-01-2025
Ff een heel lang stukje tekst... prepare :) (Over java)

Het is me in me bol geslagen... ik wil proberen een x86 simulator te makenen. Ik wil het voorlopig laten bij een simpel programmaatje dat een 8086 kan simuleren en waarbij je alleen het geheugen en de registers kan bekijken en/of bewerken. Geen andere vorm van I/O.

Op school heb ik al een soortgelijk programma gemaakt, maar ik vindt dat het beter kan. Nu zit ik al een tijdje na te denken over hoe ik het geheugen zal implementeren. In ieder geval maak ik een klasse Memory, dat is duidelijk. De interne werking heb ik nog niet helemaal duidelijk voor mezelf. Ik heb twee alternatieven bedacht:

1. Als een .asm file wordt ingeladen, worden de instructies omgezet naar echte machinetaal, zoals die er ook uitziet voor een 8086. Als de processor een instructie wil uitvoeren, haalt hij het uit het geheugen en zet de machinecode om in een instructie.
Het voordeel van deze aanpak is dat het zo dicht mogelijk bij de werkelijkheid komt. Ook is het mogelijk voor programma's zelf machinecode te laden. Ik denk dat dat laatste best belangrijk zal zijn als ik het programma wil uitbreiden.
Nadeel is volgends mij dat het traag is. Elke keer moet de processor een rij getallen omzetten in een instructie.

2. Het geheugen opdelen in twee delen. Instructie geheugen en datageheugen. Vanaf de buitenkant lijkt het alsof de instructies en data in de zelfde gegevensstructuur staan, maar intern werkt het anders. Voor de data is er gewoon een flinke array van integers of zo. Voor instructies is er een array met verwijzingen naar instacties van de klasse Instructie. De klasse Memory krijgt apparte methoden voor instructies en data, dus:
- setInstruction(Adres a, Instruction i)
- setData(Adres a, Data d)
en natuurlijk ook de getters. Als je een instructie van adres 000F:0101 ofzo wilt hebben, wordt dat adres eerst gewoon in de array voor data opgezocht. Het nummer wat daarin staat geeft aan welk element van de array voor instructies naar de instructie wijst. (Volgen jullie het nog :?)
Voordeel hiervan is dus dat al het werk om instructies te 'begrijpen' wordt gedaan tijdens het laden. Sneller dus. Nadeel is dat programma's geen code kunnen laden.


Ik weet absoluut niet welke ik moet kiezen. Ik wil wel rekening houden met evt. uibreidingen in de toekomst, bijvoorbeeld BIOS service routines, etc. Hoe zouden jullie het aanpakken? Of hebben jullie andere alternatieven?

  • Onno
  • Registratie: Juni 1999
  • Niet online
Leuk. :)

Ik snap niet helemaal wat precies je bedoeling is met optie 2, maar ik vind het niet echt handig klinken.
Nadeel is volgens mij dat het traag is. Elke keer moet de processor een rij getallen omzetten in een instructie.
Ik denk dat dat reuze meevalt. Het decoderen van een x86 instructie is niet echt ingewikkeld, en je zou natuurlijk een kleine cache kunnen maken waarin je een stukje gedecodeerde code bijhoudt. (kan loopjes behoorlijk versnellen)

Java is trouwens niet de meest geschikte omgeving om een x86 emulator in te bouwen. Maar het is wel leuk om te doen natuurlijk. :)

  • johnwoo
  • Registratie: Oktober 1999
  • Laatst online: 16:48

johnwoo

3S-GTE

Was MicroSim niet genoeg? :P

4200Wp ZO + 840Wp ZW + 1680Wp NW | 14xIQ7+ + 1xDS3-L | MTVenusE | HWP1


  • ewasx
  • Registratie: Augustus 2000
  • Laatst online: 16-11-2025
Ik weet niet wat "omzetten in een instructie" van een stukje machinecode precies inhoudt, zou je dat uit kunnen leggen? De normale manier om een emulator te schrijven is gewoon een HEEEEL groot switch/case statement.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 16-09 23:17

.oisyn

Moderator Devschuur®

Demotivational Speaker

ik zou kiezen voor variant 1, vooral ook omdat het in het echt ook zo werkt.

Jij zegt bij variant 2 dat intern de data en instructie-delen gescheiden zijn... dat is niet zo. In principe weet je van te voren niet of een stukje data nou echt daadwerkelijk data is of dat het op een gegeven moment uitgevoerd gaat worden. Bovendien kun je met variant 2 geen instructies als data inlezen (maar dit is natuurlijk wel makkelijk aan te passen), en ook geen instructies tijdens de loop van het programma wijzigen (self-modifying code, dat is lastig bij variant 2 omdat je in het ergste geval al je al gedecodeerde instructies naar de prullenbak kunt verwijzen en opnieuw moet decoderen)

Hmm... nu ik eraan denk gaat variant 2 sowieso niet werken, omdat je (zoals ik al zei) niet van te voren weet wat een instructie is of niet. En als je je programma bijvoorbeeld vanaf 1 byte later gaat decoderen dan komt er een heel andere reeks uit.

Maar het idee is leuk, succes! :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • tekno
  • Registratie: September 2001
  • Laatst online: 23-08 17:26
Maar hoe ben je dan nu het emuleren/simuleren aan het aanpakken? door middel van een interpreting cpu core of door middel van core met dynamic recompilation? en kun je misschien je 2 methodes van geheugen ietwat beter uitleggen?
Want het staat er nogal vrij vaag.... (of ik ben te dom :?)

  • JayTaph
  • Registratie: Oktober 1999
  • Laatst online: 28-11-2025

JayTaph

Portability is for canoes.

Wat je dus zou kunnen doen is om een gehele kopie van de processor te bouwen net zoiets als bochs dat doet. Als je dus alle "moeilijke" dingen zoals caching, prefetching enzo weg laat, en instructie voor instructie gaat ophalen:

(proc is een structure met alle registers uit je 8086.)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
fetch_instruction (void) {
  offset = proc[cs] * 16 + proc[ip];  // (cs << 4 + ip)

  instruction = memory[offset];

  switch (instruction) {
    case "90h" :    // NOP
              __nop ();
    .... etc  ....
  }
}

__cli (void) {
  set_flags (FLAGS_INTERRUPT, 0);
}

__sti (void) {
  set_flags (FLAGS_INTERRUPT, 1);
}

__nop (void) {
  // Doe niets
}

// Short jump
__jmp (void) {
  operand = fetch_operand ();
  proc[ip] += operand;
}

// Jump if not zero
__jnz (void) {
  if (getflags (FLAGS_ZERO) == 0) {
    operand = fetch_operand ();
    proc[ip] += operand;
  }
}


// Jump if zero
__jz (void) {
  if (getflags (FLAGS_ZERO) == 1) {
    operand = fetch_operand ();
    proc[ip] += operand;
  }
}


__mov-al (void) {
  operand = fetch_operand ();
  proc[al] += operand;
  if (proc[al]==0) set_flags (FLAGS_ZERO, 1);
  ... etcetera...
}

Naja,.. joe ket tu pojnt.

Elke instructie heeft zijn eigen methode van afhandelen. An sich zou dit dus al heel effectief moeten werken. Je bent alleen de werking van de processor hiermee aan het simuleren.

Yo dawg, I heard you like posts so I posted below your post so you can post again.


  • metaal
  • Registratie: Juli 2001
  • Laatst online: 31-01-2025
Sorry mensen dat ik niet snel reageerde, zat ff zonder internet :(

Ik denk idd dat ik maar beter methode 1 kan gebruiken. Zoals OiSyN zei is het dus niet mogelijk om het draaiende programma zelf instructies te laten laden. Met die cach is snelheid misschien geen probleem

Iemand vroeg waarom ik het in Java maak: het lijkt me juist geinig dat je intel progies op een ander besturingssysteem kan draaien :)

Verder kan ik ff niet lullen, heb op het moment ff weinig tijd, maar allemaal bedankt voor de reacties.

(Ennuuhh, johnwoo, ik het last van het Not Invented Here-syndroom 8-))
Pagina: 1