Door Dustin Hendriks

Freelanceredacteur

De virtuele machine CHIP-8

Introductie in de ontwikkeling van emulators

19-09-2022 • 06:00

19

Multipage-opmaak

Inleiding

In dit artikel helpen we je op weg bij het programmeren van een CHIP-8-emulator. We leggen kort uit wat CHIP-8 is en hoe het is ontwikkeld, en geven aan wat je nodig hebt om ermee aan de slag te gaan zodat je zelf een start kunt maken in de wereld van emulators. Daarvoor is wel noodzakelijk dat je over programmeerkennis beschikt.

CHIP-8 is een geïnterpreteerde programmeertaal en virtuele machine die in de jaren 70 werd ontwikkeld door de onderzoeker Joseph Weisbecker. De taal werd geïntroduceerd om op een toegankelijke manier programma's en spellen voor de computer te kunnen maken. Waar voorheen machinetaal voor de specifiek gebruikte hardware nodig was, konden vanaf de introductie van CHIP-8 programma's en spellen voor meerdere systemen worden ontwikkeld, met behulp van relatief eenvoudige instructies. In eerste instantie werd er ondersteuning mee geboden voor de Cosmac VIP en Telmac 1800. Daarna volgde ondersteuning voor diverse andere systemen.

Vanaf de jaren 80 werden vernieuwde interpreters voor grafische rekenmachines uitgebracht. Dat begon met de CHIP-48 voor de grafische rekenmachine HP-48. Aan de hand hiervan werden afgeleide interpreters opgesteld, waaronder de SuperChip. Deze SuperChip maakte gebruik van een grotere instructieset en hogere resolutie, terwijl hij compatibel was met de originele CHIP-8-software.

CHIP-8 wordt tegenwoordig regelmatig gebruikt om de principes van een emulator uit te leggen. Door de relatief kleine instructieset en beperkte specificaties biedt de ontwikkeling van een CHIP-8-emulator in korte tijd de benodigde kennis om grotere emulators te ontwikkelen. Dit maakt CHIP-8 een goede opstap naar emulators voor geavanceerdere spelcomputers, waarbij aan producten van bijvoorbeeld Atari, Sega, Nintendo, PlayStation of Xbox kan worden gedacht.

De lange levensduur van CHIP-8 heeft er onder andere voor gezorgd dat een grote bibliotheek aan spellen (roms) online beschikbaar is. Dat zorgt voor voldoende materiaal om een zelfontwikkelde emulator te testen. Bovendien kan hierdoor snel de impressie van een zelfgebouwde spelcomputer worden bereikt.

CHIP-8 IBM Logo ROM [10364]
IBM-logo in programma (rom) voor CHIP-8

Specificaties

Om een emulator te schrijven, is het van belang dat de details omtrent de hardware bekend zijn. In het geval van CHIP-8 is dit geen fysieke, maar virtuele hardware. Het principe blijft echter hetzelfde; in de software moeten zaken zoals registers, geheugen, display en keypad worden opgenomen, zodanig dat de hardware exact overeenkomt met het origineel. Als referentie is gebruikgemaakt van Cowgods Chip-8 Technical Reference. De specificaties voor de virtuele machine CHIP-8 zijn in dit hoofdstuk opgenomen.

Geheugen

Deze virtuele machine beschikt over 4.096 bytes aan werkgeheugen, vanaf adres 0x000 (0) tot 0xFFF (4096). Het geheugen is toegankelijk vanaf 0x200; de eerste 512 bytes worden in beslag genomen door relevante data voor de interpreter. Hieronder valt onder andere een karakterset. De volgende geheugenadressen zijn relevant:

Adres Toepassing
0x000 Start interpreter-data
0x1FF Einde interpreter-data
0x200 Start van programma
0xFFF Einde van programma

Registers

De virtuele machine CHIP-8 beschikt over verschillende registers. Registergeheugen is geïntegreerd geheugen dat gebruikt wordt in een processor, om waarden op te slaan waar de processor direct toegang toe moet hebben om instructies te kunnen voltooien. De volgende registers zijn van toepassing:

Naam Adres Toepassing Datatype
V V[0] tot en met V[F] Dataregister Array (16 * 8-bit)
SS SS[0] tot en met SS[F] Stack Segment Array (16 * 16-bit)
I 0 Indexregister 16-bit
DT 0 Delay Timer 8-bit
ST 0 Sound Timer 8-bit
PC 0 Program Counter 16-bit
SP 0 Stack Pointer 8-bit

Alle datatypes zijn unsigned; het zijn natuurlijke getallen die lopen vanaf 0 tot aan de grootte gerelateerd aan het aangegeven datatype.

Display

CHIP-8 maakt gebruikt van een monochroom display met een breedte van 64 pixels en een hoogte van 32 pixels. Daarbij is van belang dat de linkerbovenhoek de coördinaat (0,0) heeft en de rechteronderhoek de coördinaat (63,31). De hoeken van het display kunnen daarom op de volgende manier worden gerepresenteerd:

Display Coördinaat links (x) Coördinaat rechts (x)
Coördinaat boven (y) (0,0) (63,0)
Coördinaat onder (y) (0,31) (63,31)

Deze coördinaten zijn van belang voor de koppeling tussen de virtuele hardware en de programmatuur om de pixels weer te geven op een venster of display naar keuze.

Keypad

CHIP-8 biedt ondersteuning voor een hexadecimaal toetsenbord met zestien knoppen. De volgende indeling is van toepassing:

1 2 3 C
4 5 6 D
7 8 9 E
A 0 B F

Karakterset

CHIP-8 beschikt standaard over zestien voorgeprogrammeerde tekens (0 tot en met F). Deze karakterset wordt gebruikt door verschillende programma's. Ieder karakter heeft een grootte van vijf bytes. De karakterset moet worden ingeladen op geheugenadres 0x000 tot en met 0x04F, wat gebaseerd is op het aantal karakters en de grootte per karakter (sprite).

De karakters worden getekend door per byte te definiëren welke bits aan- of uitstaan. De karakterset die geïmplementeerd moet worden, is hieronder zichtbaar:

'0' Binair Hexadecimaal '1' Binair Hexadecimaal

[][][][]
[]__[]
[]__[]
[]__[]
[][][][]

0b11110000
0b10010000
0b10010000
0b10010000
0b11110000
0xF0
0x90
0x90
0x90
0xF0
__[]_
_[][]_
__[]_
__[]_
_[][][]
0b00100000
0b01100000
0b00100000
0b00100000
0b01110000

0x20
0x60
0x20
0x20
0x70

'2' Binair Hexadecimaal '3' Binair Hexadecimaal
[][][][]
___[]
[][][][]
[]___
[][][][]
0b11110000
0b00010000
0b11110000
0b10000000
0b11110000

0xF0
0x10
0xF0
0x80
0xF0

[][][][]
___[]
[][][][]
___[]
[][][][]
0b11110000
0b00010000
0b11110000
0b00010000
0b11110000
0xF0
0x10
0xF0
0x10
0xF0
'4' Binair Hexadecimaal '5' Binair Hexadecimaal
[]__[]
[]__[]
[][][][]
___[]
___[]
0b10010000
0b10010000
0b11110000
0b00010000
0b00010000
0x90
0x90
0xF0
0x10
0x10
[][][][]
[]___
[][][][]
___[]
[][][][]
0b11110000
0b10000000
0b11110000
0b00010000
0b11110000

0xF0
0x80
0xF0
0x10
0xF0

'6' Binair Hexadecimaal '7' Binair Hexadecimaal
[][][][]
[]___
[][][][]
[]__[]
[][][][]
0b11110000
0b10000000
0b11110000
0b10010000
0b11110000
0xF0
0x80
0xF0
0x90
0xF0
[][][][]
___[]
__[]_
_[]__
_[]__
0b11110000
0b00010000
0b00100000
0b01000000
0b01000000
0xF0
0x10
0x20
0x40
0x40
'8' Binair Hexadecimaal '9' Binair Hexadecimaal
[][][][]
[]__[]
[][][][]
[]__[]
[][][][]
0b11110000
0b10010000
0b11110000
0b10010000
0b11110000
0xF0
0x90
0xF0
0x90
0xF0

[][][][]
[]__[]
[][][][]
___[]
[][][][]

0b11110000
0b10010000
0b11110000
0b00010000
0b11110000
0xF0
0x90
0xF0
0x10
0xF0
'A' Binair Hexadecimaal 'B' Binair Hexadecimaal

[][][][]
[]__[]
[][][][]
[]__[]
[]__[]

0b11110000
0b10010000
0b11110000
0b10010000
0b10010000

0xF0
0x90
0xF0
0x90
0x90

[][][]_
[]__[]
[][][]_
[]__[]
[][][]_

0b11100000
0b10010000
0b11100000
0b10010000
0b11100000

0xE0
0x90
0xE0
0x90
0xE0
'C' Binair Hexadecimaal 'D' Binair Hexadecimaal
[][][][]
[]___
[]___
[]___
[][][][]
0b11110000
0b10000000
0b10000000
0b10000000
0b11110000
0xF0
0x80
0x80
0x80
0xF0

[][][]_
[]__[]
[]__[]
[]__[]
[][][]_

0b11100000
0b10010000
0b10010000
0b10010000
0b11100000
0xE0
0x90
0x90
0x90
0xE0
'E' Binair Hexadecimaal 'F' Binair Hexadecimaal
[][][][]
[]___
[][][][]
[]___
[][][][]

0b11110000
0b10000000
0b11110000
0b10000000
0b11110000

0xF0
0x80
0xF0
0x80
0xF0
[][][][]
[]___
[][][][]
[]___
[]___

0b11110000
0b10000000
0b11110000
0b10000000
0b10000000

0xF0
0x80
0xF0
0x80
0x80

Instructieset

De instructieset van een processor is een verzameling van alle mogelijke machinecodes die door die processor kunnen worden verwerkt. Voor een emulator is het essentieel dat de instructieset correct is opgenomen zodat programma's goed functioneren.

Uitlezen van de instructie

Iedere instructie heeft een omvang van 2 bytes. De bytevolgorde die hierbij van toepassing is, betreft Big-endian. Dit is een term die aangeeft dat de meest significante bit als eerste staat genoteerd, waarbij wordt afgeteld in significantie gerelateerd aan het aantal bits.

Meest significante byte (msb) Minst significante byte (lsb)
Positie Eerste byte van instructie Tweede byte van instructie
Voorbeeld 0 0 0 0 0 0 0 0 1 1 1 0 1 1 1 0

Een correcte representatie van de instructie vereist een 16bit-datatype.

Instructie (16-bit) 0 0 0 0 0 0 0 0 1 1 1 0 1 1 1 0

Operatiecode en operanden

Een instructie kan worden opgesplitst in twee delen. De operatiecode (opcode) geeft aan welke bewerking moet worden uitgevoerd, terwijl de operanden de invoerwaarden voor een instructie definiëren. Een operand kan data bevatten, maar ook een geheugenadres.

De operanden zijn relevant voor de uiteindelijke verwerking van de instructieset. De volgende operanden zijn van toepassing bij CHIP-8:

Naam Instructie waarde
nnn Minst significante 12 bits
n Minst significante 4 bits (nibble)
x Minst significante 4 bits van de meest significante 8 bits (byte)
y Meest significante 4 bits van de minst significante 8 bits (byte)
kk Minst significante 8 bits (byte)

Instructieset

De originele implementatie van CHIP-8 bevat 36 verschillende instructies. Hieronder vallen onder andere wiskundige operaties, keuzestructuren, grafische instructies en declaraties. Deze instructies zijn hieronder uiteengezet met de operatiecode en operanden, mnemonic, definitie en uitleg.

Operatiecode en operanden Mnemonic Definitie Uitleg
00E0 CLS Leegmaken van het display. Alle bits van het display worden toegekend op de waarde '0'.
00EE RET

Terugkeren van een subroutine.

De Program Counter ('PC') wordt naar het adres bovenaan het Stack Segment ('SS') gezet. De Stack Pointer ('SP') wordt verlaagd met 1.
1nnn JP nnn Spring naar locatie nnn. De Program Counter ('PC') wordt gezet naar de operand 'nnn'.
2nnn CALL nnn Subroutine-aanroep op nnn. De Stack Pointer ('SP') wordt verhoogd met 1. De huidige Program Counter ('PC') wordt op het Stack Segment ('SS') gezet.
3xkk SE V[x], kk Overslaan van de volgende instructie, indien V[x] gelijk is aan kk. De waarde van het register 'V[x]' wordt vergeleken met de operand 'kk'. Indien ze overeenkomen, wordt de Program Counter ('PC') verhoogd met 2.
4xkk SNE V[x], kk Overslaan van de volgende instructie indien V[x] ongelijk is aan kk. De waarde van het register 'V[x]' wordt vergeleken met de operand 'kk'. Indien ze niet overeenkomen, wordt de Program Counter ('PC') verhoogd met 2.
5xy0 SE V[x], V[y] Overslaan van de volgende instructie indien V[x] gelijk is aan V[y]. De waarde van het register 'V[x]' wordt vergeleken met de waarde van het register 'V[y]'. Indien ze overeenkomen, wordt de Program Counter ('PC') verhoogd met 2.
6xkk LD V[x], kk Toekennen van V[x] = kk. De waarde van de operand 'kk' wordt toegekend aan het register 'V[x]'.
7xkk ADD V[x], kk Toekennen van V[x] = V[x] + kk. De waarde van de operand 'kk' wordt opgeteld bij de waarde van het register 'V[x]' en toegekend aan het register 'V[x]'.
8xy0 LD V[x], V[y] Toekennen van V[x] = V[y]. De waarde van het register 'V[y]' wordt toegekend aan het register 'V[x]'.
8xy1 OR V[x], V[y] Toekennen van V[x] = V[x] ∨ V[y]. De waarde van 'V[x] OR V[y]' wordt toegekend aan het register 'V[x]'.
8xy2 AND V[x], V[y] Toekennen van V[x] = V[x] ∧ V[y]. De waarde van 'V[x] AND V[y]' wordt toegekend aan het register 'V[x]'.
8xy3 XOR V[x], V[y] Toekennen van V[x] = V[x] ^ V[y]. De waarde van 'V[x] OR V[y]' wordt toegekend aan het register 'V[y]'.
8xy4 ADD V[x], V[y] Toekennen van V[x] = V[x] + V[y]. V[F] = Carry. De waarde van 'V[x] + V[y]' wordt bepaald. Indien het resultaat groter is dan '255', zal het register 'V[F]' op '1' worden gezet; anders op '0'. De minst significante byte van de optelling wordt toegekend aan het register 'V[F]'.
8xy5 SUB V[x], V[y] Toekennen van V[x] = V[x] - V[y]. V[F] = ¬Borrow. Indien de waarde van het register 'V[x]' groter is dan de waarde van het register 'V[y]', zal het register 'V[F]' op '1' worden gezet; anders op '0'. De waarde van 'V[x] - V[y]' wordt toegekend aan het register 'V[x]'.
8xy6 SHR V[x] Toekennen van V[x] = V[x] >> 1. V[F] = LSB == 1. Indien de minst significante bit van het register 'V[x]' gelijk is aan '1', zal het register 'V[F]' op '1' worden gezet; anders op '0'. De waarde van 'V[x] / 2' wordt toegekend aan het register 'V[x]'.
8xy7 SUBN V[x], V[y] Toekennen van V[x] = V[y] - V[x]. V[F] = ¬Borrow. Indien de waarde van het register 'V[y]' groter is dan de waarde van het register 'V[x]', zal het register 'V[F]' op '1' worden gezet; anders op '0'. De waarde van 'V[y] - V[x]' wordt toegekend aan het register 'V[x]'.
8xyE SHL V[x] Toekennen van V[x] = V[x] << 1. V[F] = LSB == 1. Indien de meest significante bit van het register 'V[x]' gelijk is aan '1', zal het register 'V[F]' op '1' worden gezet; anders op '0'. De waarde van 'V[x] * 2' wordt toegekend aan het register 'V[x]'.
9xy0 SNE V[x], V[y] Overslaan van de volgende instructie indien V[x] ongelijk is aan V[y]. De waarde van het register 'V[x]' wordt vergeleken met de waarde van het register 'V[y]'. Indien deze niet overeenkomen, wordt de Program Counter ('PC') verhoogd met 2.
Annn LD I, nnn Toekennen van I = nnn. De waarde van de operand 'nnn' wordt toegekend aan het register 'I'.
Bnnn JP V[0], nnn Spring naar locatie nnn + V[0]. De waarde van 'V[0]+nnn' wordt toegekend aan de Program Counter ('PC').
Cxkk RND V[x], kk Toekennen van V[x] = RND ∧ kk.

De interpreter genereert een willekeurig getal van 0 tot en met 255, genaamd 'RND'.

De waarde 'RND AND kk' wordt toegekend aan het register 'V[x]'.

Dxyn DRW V[x], V[y], n Weergeven van sprite op geheugenlocatie I op coördinaat (V[x], V[y]), V[F] = Overschreven vlag De interpreter leest 'n' bytes uit het geheugen, beginnend bij het adres opgeslagen in register 'I'. Sprites worden middels de XOR-operatie weergegeven op het display. Indien pixels zijn verwijderd, zal het register 'V[F]' op '1' worden gezet; anders op '0'. Indien de x- en/of y-coördinaat van een pixel buiten het scherm valt, begint hij weer aan de andere kant.
Ex9E SKP V[x] Overslaan van de volgende instructie, indien V[x] gelijk is aan een ingedrukte toets. Indien een knop van het hexadecimale keypad met de waarde uit het register 'V[x]' is ingedrukt, wordt de Program Counter ('PC') verhoogd met 2.
ExA1 SKNP V[x] Overslaan van de volgende instructie indien V[x] ongelijk is aan een ingedrukte toets. Indien geen knop van het hexadecimale keypad met de waarde uit het register 'V[x]' is ingedrukt, wordt de Program Counter ('PC') verhoogd met 2.
Fx07 LD V[x], DT Toekennen van V[x] = DT. De waarde van de delay timer ('DT') wordt toegekend aan het register 'V[x]'.
Fx0A LD V[x], K Toekennen van V[x] = toetsinvoer (blokkeert). Er wordt gewacht op invoer van het toetsenbord (blokkeert). Bij invoer wordt de waarde toegekend aan het register 'V[x]'.
Fx15 LD DT, V[x] Toekennen van DT = V[x]. De waarde van het register 'V[x]' wordt toegekend aan de Delay Timer ('DT').
Fx18 LD ST, V[x] Toekennen van ST = V[x]. De waarde van het register 'V[x]' wordt toegekend aan de Sound Timer ('ST').
Fx1E ADD I, V[x] Toekennen van I = I + V[x]. De waarde van 'I + V[x]' wordt toegekend aan het register 'I'.
Fx29 LD F, V[x] Toekennen van sprite-locatie voor V[x] aan I. De hexadecimale sprite die correspondeert met de waarde van het register 'V[x]' wordt toegekend aan het register 'I'.
Fx33 LD B, V[x] Toekennen van V[I], V[I+1] en V[I+2] als binair gecodeerde decimaal.

De honderdtallen van de waarde van het register 'V[x]' worden geplaatst in 'V[I]'.
De tientallen van de waarde van het register 'V[x]' worden geplaatst in 'V[I+1]'.
De eenheden van de waarde van het register 'V[x]' worden geplaatst in 'V[I+2]'.

Fx55 LD I, V[x] Toekennen van V[i] aan geheugen Memory[I+i]. De waarden van de registers 'V[0]' tot en met 'V[x]' worden gekopieerd naar het geheugen 'Memory', beginnend bij operand 'I'.
Fx65 LD V[x], [I] Toekennen van Memory[I+i] aan V[i]. De waarden van de registers 'Memory[I]' tot en met 'Memory[I+x]' worden gekopieerd naar het register 'V', beginnend bij 'V[0]'.

Programmeren

Voorafgaand aan dit artikel is een CHIP-8-emulator geschreven in C# in combinatie met het WPF-framework. De belangrijkste onderdelen van de code zullen in dit hoofdstuk worden toegelicht. De complete code kan worden gevonden op GitHub, onder CHIP-8-Manager. Het is ook mogelijk om alleen met het programma zelf te experimenteren. Het kan worden gedownload onder de gepubliceerde versies.

Je kunt ervoor kiezen om eerst zelf aan de slag te gaan met het schrijven van code, voordat je de voorbeelduitwerking in dit hoofdstuk raadpleegt.

Geheugen

CHIP-8 Memory Implementation [10364]CHIP-8 beschikt over 4KB, of 4.096 bytes, aan geheugen. Programma's (roms) worden in het geheugen geladen, zoals omschreven in het hoofdstuk Specificaties. Deze programma's bestaan uit verschillende instructies die door CHIP-8 moeten worden uitgevoerd. Aangezien iedere instructie over een lengte van twee bytes beschikt, is het handig om een functie te schrijven waarmee ze worden opgehaald uit het geheugen. Dat kan door bij het individueel uitlezen onderscheid te maken tussen de meest significante byte (msb) en minst significante byte (lsb). Deze twee bytes kunnen samen als returnwaarde worden opgenomen in een functie. Deze returnwaarde vereist een datatype van ten minste twee bytes (unsigned), anders kunnen niet alle bits worden opgenomen in het resultaat. Op basis van de index die als parameter wordt meegegeven, kan de betreffende instructie worden uitgelezen.

Registers

CHIP-8 Registry Implementation [10364]

De verschillende registers zoals die zijn uiteengezet onder het hoofdstuk Specificaties kunnen worden ondergebracht in één klasse. De volgende registers kunnen worden ingevoerd:

  • Dataregister (V)
  • Indexregister (I)
  • Stack Segment (SS)
  • Delay Timer (DT)
  • Sound Timer (ST)
  • Program Counter (PC)
  • Stack Pointer (SP)

Binnen de referentie voor de instructieset komt met regelmaat het verlagen en verhogen van de Stack Pointer terug. Een alternatief om deze operatie veel terug te laten komen, is om een push en pop-functie te schrijven, waarbij de Stack Pointer eenmalig wordt verlaagd en verhoogd. Gelijktijdig kan in de functie een waarde op de stack worden bijgewerkt of worden geretourneerd.

Display

CHIP-8 Display Implementation [10364]

CHIP-8 maakt gebruikt van een monochroom display met een breedte van 64 pixels en een hoogte van 32 pixels. Daarbij is van belang dat voor de linkerbovenhoek de coördinaat (0,0) van toepassing is, terwijl voor de rechtsonderste hoek de coördinaat (63,31) geldt. Vanwege het monochrome display kan voor iedere x- en y-positie worden aangegeven of de pixel aan- of uitstaat. Er hoeft dus geen rekening te worden gehouden met verschillende kleuren op het scherm.

Binnen de displayklasse is een functie opgenomen voor het tekenen van een karakter (sprite). Als een sprite uit het scherm valt, dus wanneer de x- en/of y-coördinaat van een pixel buiten het bereik van die as valt, begint hij weer aan de andere kant.

Verder is er een aantal hulpfuncties toegevoegd die de aansturing vereenvoudigen. Zo is er een functie om het display te legen en om een specifieke pixel aan of uit te zetten.

Keypad

CHIP-8 Keypad Implementation [10364]CHIP-8 biedt ondersteuning voor een hexadecimaal toetsenbord met zestien knoppen. Een voor de hand liggende implementatie is om op basis van de index te registreren of een toets wel of niet is ingedrukt. Middels een achterliggende implementatie voor een fysiek of virtueel toetsenbord, afhankelijk van het besturingssysteem en UI-framework, kan de betreffende toets worden geregistreerd als 'ingedrukt' of 'losgelaten'.

Veel emulatoren bieden de mogelijkheid van een virtueel toetsenbord dat zichtbaar is op het scherm, naast de mogelijkheid om fysieke toetsen te gebruiken. Deze fysieke toetsen komen in veel gevallen overeen met een afdeling op het toetsenbord. De indeling is qua vorm gelijk aan die van de inrichting van het keypad-design. Op die manier kan de besturing als intuïtiever worden ervaren dan met een letterlijke hexadecimale configuratie van 0 tot en met F.

Karakterset

CHIP-8 Character Implementation [10364]De karakterset is overgenomen zoals omschreven bij de specificaties, met uitzondering van de extra letter 'K'. Deze letter is toegevoegd zodat 'OK' kan worden weergegeven in het standaard programma.

De letters zijn opgebouwd door te definiëren welke pixels (bits) aan- of uitstaan. Ieder karakter heeft een grootte van vijf bytes. Officieel zijn totaal zestien karakters gedefinieerd, maar het geheugen is groot genoeg om er nog extra toe te voegen.

Operatiecode en operanden

CHIP-8 Operands Implementation [10364]

Zoals omschreven in het hoofdstuk 'Instructieset' zijn vijf operanden benodigd om de instructies uit te kunnen voeren. Deze vijf operanden kunnen bijvoorbeeld op deze manier worden geïnterpreteerd.

Instructieset

CHIP-8 Core Implementation [10364]De klasse CoreData brengt de instructieset en essentiële data samen. Daaronder vallen het geheugen, register, toetsenbord en display. Deze klasse implementeert ook de instructieset en vormt de basis voor de emulator.

Koppeling

De volgende stap is het schrijven van code om het geheel aan te sturen. Daar komt ook een implementatie voor video, audio en toetsenbordinvoer bij kijken. Deze implementatie is afhankelijk van het besturingssysteem en kan worden vereenvoudigd door het gebruik van diverse softwarebibliotheken. De broncode kan worden bekeken om te zien hoe dit middels C# en het WPF-framework is gedaan.

Testen

Een lastig aspect bij het programmeren van een CHIP-8-emulator kan de verificatie zijn. In sommige gevallen zijn fouten niet direct zichtbaar. Door veel roms uit te proberen, kunnen fouten worden opgespoord. Bovendien zijn er testprogramma's beschikbaar die exact aangeven of instructies correct zijn geïmplementeerd.

CHIP-8 Opcode Test [10364]

Conclusie

De CHIP-8-emulator is gelimiteerd en de software vaak onpraktisch. CHIP-8 wordt dan ook vooral gebruikt voor entertainment en educatieve doeleinden.

Voor dat laatste is de software prima geschikt, want het creëren van een CHIP-8-emulator is een ideale manier om ervaring te krijgen met de werking van emulators. Daarnaast is het een geschikt project om te oefenen met bitwise-bewerkingen en het begrijpen van de basis van een computer. Het bouwen van een CHIP-8-emulator is dan ook een veelgebruikte eerste stap om vervolgens meer uitdagende projecten aan te pakken, zoals het programmeren van een emulator voor een spelcomputer.

In dit artikel hebben we een eerste start beschreven om met emulators aan de slag te gaan. Online is er veel materiaal te vinden waarvan je kunt leren en mee verder kunt experimenteren. Wellicht heb jij al een emulator geschreven of ga je daar in de toekomst mee aan de slag. Laat het ons en je medetweakers weten in de comments onder dit artikel.

emulator

Reacties (19)

19
19
7
3
0
6
Wijzig sortering
Grappig om dit artikel te lezen. Zo'n 9 maanden geleden was ik op zoek naar een niet al te ingewikkeld project om mijn eerste emulator (in de awk scripttaal) te maken en kwam toevalligerwijs ook uit op CHIP-8
Als je iets van programmeren af weet, is het zeker goed te doen om je eerste emulator te schrijven. Voor de geïnteresseerden: https://github.com/patsie75/awk-chip8
Zeker! Een vriend van mij heeft ook een mooie geschreven een tijd geleden. Zie hier de github. Dat was het eerste wat bij me opkwam. Leuk om ook dit soort artikelen te lezen op tweakers!
Vi gebruiker hier. Ik heb vaak over awk gelezen maar weinig nog mee gedaan.
Ik vond het ook jammer dat het artikel naar Windows / C# verwees. Ben veel meer fan van Unix. De filosofie van de pipeline van simpele programma's zoals cut, grep, sed, awk etc. is geweldig en je kan er prima wiskunde en informatica mee duidelijk maken.
Wat ik nooit eerder begrepen heb is waarvoor de shift operatie was maar door MIT 6.006 (introduction to algorithms, open course ware, gratis) weet je bvb. dat shift voor binair delen en vermenigvuldigen wordt gebruikt. Dat maakt heap sorteren en binaire bomen zo efficiënt.
Ik vond het ook jammer dat het artikel naar Windows / C# verwees.
C# is al jaaaaren niet meer alleen aan Windows verbonden maar draait ook prima op MacOS en Linux.

[Reactie gewijzigd door RobIII op 22 juli 2024 15:40]

Weet ik maar Unix, shell, C & tools zijn toch wel de low level gereedschappen die toch veel langer worden gebruikt. Het zal wel aan de spelletjes natuur van het beestje liggen...

[Reactie gewijzigd door n0np3r50n op 22 juli 2024 15:40]

Het zal wel aan de spelletjes natuur van het beestje liggen...
Welke spelletjes natuur van welk beestje :?
Op Windows worden voornamelijk spelletjes gespeeld toch?

En sommige plaatjes lieten spelletjes zien.
Op Windows worden voornamelijk spelletjes gespeeld toch?
Okeeeeeee. 8)7 Prima. Ok. Wat jij wil d:)b :w

[Reactie gewijzigd door RobIII op 22 juli 2024 15:40]

Aanrader in hetzelfde straatje. Het Nand2Tetris project. Hierbij bouw je zelf (virtueel) een computer door te beginnen met simpelweg de logische NAND poort. In een 12-tal projecten werk je jezelf omhoog van logische schakelingen in de basis, naar het bouwen van complexe schakelingen, chips en uiteindelijk een (turing-compleet) hardware platform.
Daarna bouw je ook de software-stack door een assembler, virtuele machine, compiler en OS te bouwen. Uiteindelijk kun je op je eigen gebouwde platform een game (zoals Tetris) programmeren. Mega-leerzaam en (met wat doorzettingsvermogen) best toegankelijk.

Een deel van het materiaal is gratis beschikbaar, voor de 2e helft moet je het boek aanschaffen. Online zijn verschillende cursussen te vinden die het boeltje ondersteunen met colleges/uitleg (via Udemy e.d.)

Website is: https://www.nand2tetris.org/

Ik geen informatica op de middelbare school en ik zet uitblinkers hier mee aan de slag. Is pittig voor ze, maar superleerzaam.
Ik geen informatica op de middelbare school en ik zet uitblinkers hier mee aan de slag
Hulde hiervoor! Het hoort niet bij je werk, maar ik denk wel dat je een leerling hiermee enorm helpt.

Mijn informaticadocent kwam niet verder dan: "Doe de het hele boek nog maar een keer afwerken", waarna ik toen maar een Re-Volt LAN server ging opzetten en anderen afhield van hun werk. Ik heb aan die middagen gamen wel goede herinneringen, maar was waarschijnlijk veel meer gebaat bij extra leerstof.

[Reactie gewijzigd door DaFeliX op 22 juli 2024 15:40]

Die van mij kwam niet verder dan "Kan je dat niet Googlen?"
Grappig om hier iets over CHIP-8 te lezen. Het is al een tijdje één van mijn hobbies, en je hoort er doorgaans niet zoveel over. Een paar aanvullingen en kleine waarschuwingen voor de geïnteresseerde:
  • Er zijn veel meer varianten van CHIP-8 geweest. En de CHIP-8 smaakjes uit de grafische-rekenmachine tijd zijn helaas niet 100% compatible met de originele versie, wat maakt dat veel programma's niet goed of helemaal niet draaien op een emulator die daar geen rekening mee houdt. Voor een overzicht van de verschillende versies en wat ze anders maakt, zie: https://chip-8.github.io/extensions/
  • Als je zelf een emulator aan het schrijven bent is een goede test inderdaad on-ontbeerlijk. Daarom heb ik een tijdje terug een uitgebreidere test geschreven, die je ook kan vertellen of je je goed aan de verschillen tussen versies houdt: https://github.com/Timendus/chip8-test-suite. Ik ben wel nieuwsgierig of de emulator van de schrijver door deze tests komt! 😉
  • Hoewel CHIP-8 een platform uit de jaren '70 is, vindt er jaarlijks in oktober (volgende maand!) een Gamejam plaats om programmaatjes voor het platform (of één van de varianten) te schrijven. Die jam heet Octojam, en alweer de negende editie begint dus binnenkort. Doe mee! https://itch.io/jam/octojam-9/
  • Als je vastloopt is het fijn om ergens terecht te kunnen met je vragen. Er is een CHIP-8 subreddit, maar die is niet zo actief. Je kunt beter terecht bij de EmuDev subreddit, of op de EmuDev Discord.
Oeh erg tof project! Leuk toegelicht ook. Meer, Tweakers, meer!
Leuk artikel weer!.

Suggestie / feature verzoek:
Eigenlijk merk ik dat ik heel veel artikelen wel leuk en interessant vind, maar ze graag óók als video zie.
Dat was ooit een enquete van Tweakers onder Plus-abbonees meen ik me te herinneren.

Stel dat tweakers letterlijk iemand het Plus-artikel laat voorlezen in een video, ondersteund met de plaatjes en tabellen uit het artikel.
Dat zou ik kijken.

Mag van mij zelfs zoals een iemand die "gewoon" zijn scherm deelt met Tweakers.net open (met bank-TV vriendelijke fontsize) terwijl hij als pop-in te zien is. Hoeft niet ingewikkelder.

Lees gewoon alles voor, of als het erg diep gaat en niet praktisch wordt zeg "dat stukje in het artikel gaat daar dieper op in" en vat het samen. Ik zou het graag kijken/horen terwijl ik in de keuken bezig ben of bij de TV zit..

[Reactie gewijzigd door Barryke op 22 juli 2024 15:40]

Eens maar heb je niks op YouTube gevonden dan? Of gaat het je om de Nederlandse taal?
Mooie artikel, dank! _/-\o_
Leuk artikel. Zou je niet gewoon unit tests kunnen schrijven om de instructies te testen? Dan hoef je niet al die ROMs uit te proberen.

Ik zou overigens niet mijn tijd aan dit soort dingen besteden. Dan zou ik liever iets in x86 assembler bouwen zoals SnowDrop bijvoorbeeld.

Ook iets in ARM of RISC-V assembler schrijven en dat draaien in een emulator om nuttige kennis op te doen.

[Reactie gewijzigd door Godson-2 op 22 juli 2024 15:40]

Hallo Godson-2!

Zeker, in je eigen project is unit tests schrijven altijd een goed idee. Maar je wilt vaak ook graag jouw implementatie (en jouw begrip van de instructieset!) kunnen checken tegen de implementatie van anderen, zeker als je "alles goed hebt gedaan", maar Tetris toch niet goed werkt, zeg maar. Dat je net effe niet goed had geïnterpreteerd hoe die ene instructie het flags register beïnvloedt, ofzo.

En dan loop je tegen het probleem aan dat iedereen zo'n emulator in een andere taal programmeert, met een ander framework, voor een ander OS, et cetera. Dus unit tests van iemand anders copy-pasten zit er niet altijd in. Maar je kunt wel altijd een test draaien die zelf is geschreven in CHIP-8.
Zou je niet gewoon unit tests kunnen schrijven om de instructies te testen
Dat lijkt me inderdaad onontbeerlijk, zelfs voor iets simpels als de CHIP-8 instructie set.

Ik hobby zelf af en toe aan mijn eigen zelf gecreëerde fantasy console emulator, die meer lijkt op/geïnspireerd is door bestaande 16-bit consoles. Alleen al de logica voor branch instructies op basis van CPU flags is ingewikkeld genoeg dat je er echt niet zonder unit tests op wil vertrouwen dat je alles goed gedaan hebt :+

Op dit item kan niet meer gereageerd worden.