Durch Zufall bin ich auf eine recht alte Programmiersprache namens CHIP-8 gestoßen.
CHIP-8 wurde von Joseph Weisbecker, Mitarbeiter der RCA Laboratories, Princetown, Mitte der 70er-Jahre entwickelt. Ursprünglich wurde die Sprache für Heimcomputer wie den COSMAC VIP oder den TELMAC konzipiert. Ziel war es, eine einfache Sprache zur plattformunabhängigen Entwicklung von Videospielen bereitzustellen.
Der COSMAC VIP ist ein Einplatinenrechner, vergleichbar mit dem LC-80. Ein CDP-1802 8-Bit-Mikrokontroller, 2 KByte RAM, eine Hexadezimaltastatur, 1,76 MHz-Taktfrequenz, ein Kassettenrekorder dient zur Programmspeicherung. Unterschiedlich zum LC-80 ist die Anzeige. Während letzterer nur eine 6-stellige 7-Segment-Anzeige besitzt, hat der COSMAC VIP einen Grafikprozessor zum Anschluss an einen Fernseher. Es konnten 64×32 monochrome Pixel dargestellt werden.
Hergestellt wurde der COSMAC VIP bereits 1977, der LC-80 folgte erst 8 Jahre später!
Merkmal | Beschreibung |
---|---|
CPU | CDP-1802 |
ROM | 0.5 KByte |
RAM | 2 KByte |
Takt | 1,76 MHz |
Anzeige | TV, 64×32 Pixel, monochrom |
Tastatur | 16 Tasten (Hex) |
Peripherie | Kassettenrekorder |
Software | Programmiersprache CHIP-8 |
weitere Dokumente
Octo - eine moderne Chip8 IDE
CHIP-8 ist eine Maschinencode-Sprache für einen (theoretischen) 8-Bit-Prozessor. Es gibt nur 31(35) Maschinencode-Befehle; allerdings sind einige darunter, die das Programmieren von Telespielen besonders erleichtern, so z.B. eine Sprite-Ausgabe mit Kollisionserkennung oder bedingte Sprünge bei nicht-/gedrückter Taste.
Auf dem COSMAC VIP muss ein CHIP-8-Interpreter eingegeben werden. Dieser interpretiert dann den CHIP-8-Programmcode und führt so die Programme aus. Der CHIP-8-Interpreter ist extrem platzsparend programmiert, er belegt nicht einmal 0.5 KByte! Diese Kompaktheit spiegelt sich beispielsweise auch in den Hex-Werten der Maschinencode-Instruktionen wieder: Die Befehle der F-Gruppe haben einen zweistellige Nummer, diese entspricht der Startadresse der zugehörigen Befehlsinterpretation. Damit sparte man sich die Bytes für eine zusätzliche Sprungtabelle. Auch der Zeichensatz ist clever komprimiert (s. Bild und VIPER 1-01).
Einen guten Einstieg in die Programmierung mit CHIP-8 liefert das Handbuch zum COSMAC VIP (RCA COSMAC VIP CDP18S711 Instruction Manual; 130 S.). Hier sind auch 20 Spiele abgedruckt, die mit CHIP-8 laufen (PONG, TIC-TAC-TOE, SNAKE, …)
Im VIPER-Magazin 1-01 June 1978 werden zusätzliche Hinweise zur Programmierung mit CHIP-8 gegeben.
Ausgabe VIPER-Magazin 1-02 August 1978 liefert Informationen über die Arbeitsweise des CHIP-8-Interpreters. Es gibt Ablaufpläne und kommentierte Listings. → CHIP-8-Interpreter
http://mattmik.com/chip8.html beschreibt ausführlich den CHIP-8-Maschinencode.
virtueller Prozessor | 16 Register V0..VF Index-/Adressregister I (Stackregister SP) (Program Counter PC) Delay Timer DT Sound Timer ST |
Grafik | 64×32 Pixel, Torus |
Tastatur | 4×4-Tastenfeld |
Sound | Beepton |
Der originale CHIP-8-Interpreter ist eine coole Software, umfasst nur 500 Byte, davon 1/3 für Sprite-Befehl! Es sind viele speicherplatzsparende Programmkniffe enthalten, ein Blick in den Quellcode lohn sich! z.B. Fx-Befehle, Bitmuster der Hexziffern
Die Virtuelle Maschine
Die CHIP-8 Speicher-Adressen liegen im Bereich von 200h bis FFFh, das reicht für 3.584 Bytes. Der Grund für den Speicher ab 200h ist, dass im VIP Cosmac und Telmac 1800 die ersten 512 Byte für den CHIP8-Interpreter reserviert sind. Auf diesen Maschinen wurden die obersten 256 Bytes (F00h-FFFh auf einem 4K-Maschine) für die Anzeige aktualisieren vorbehalten, und die 96 Byte unterhalb (EA0H-EFFh) wurden für den Call-Stack, den internen Gebrauch, und die Variablen vorbehalten.
CHIP-8 verfügt über 16 8-Bit-Register V0..VF. Das VF-Register dient auch als Carry-Flag.
Der Stack wird nur verwendet, um die Rückkehr-Adressen zu speichern, wenn Unterprogramme aufgerufen werden. Original ist Speicher für bis zu 12 Verschachtelungsebenen vorhanden.
CHIP-8 verfügt über zwei Timer. Beide werden automatisch mit 60 Hz dekrementiert, bis sie 0 erreichen. Delay Timer DT: Dieser Timer soll für das Timing der Ereignisse von Spielen verwendet werden. Sein Wert kann eingestellt und gelesen werden. Sound- Timer ST: Dieser Timer ist für Sound-Effekte gedacht. Solange der Wert ungleich Null ist, wird ein Piepton erzeugt.
Die Eingabe erfolgt mit einer Hex-Tastatur mit 16 Tasten von 0 bis F. '8', '4', '6 'und '2' dienen in der Regel als Cursortasten. Es gibt drei Opcodes zur Tastaturabfrage. Eine überspringt eine Anweisung, wenn eine bestimmte Taste gedrückt wird, eine weitere überspringt eine Anweisung, wenn eine bestimmte Taste nicht gedrückt wird. Die dritte wartet auf einen Tastendruck, und speichert die Taste dann in einem der Datenregister.
Die Display-Auflösung beträgt 64 × 32 Pixel, und die Farbe ist einfarbig. Grafiken werden auf dem Bildschirm allein durch Sprites gezeichnet, die 8 Pixel breit sind und von 1 bis 15 Pixel hoch sein können. Sprite-Pixel, die gesetzt sind, invertieren die Farbe der entsprechenden Bildschirm-Pixel, während nicht gesetzten Sprite-Pixel nichts verändern.
Wenn beim Zeichnen des Sprites alle Bildschirm-Pixel invertiert wurden, wird das Carry-Flag (VF) wird auf 1 gesetzt, sonst ist es 0.
Wie zuvor beschrieben, wird ein Signalton abgespielt, wenn der Wert der Sound Timer ungleich Null ist.
CHIP-8 verfügt über 35 Opcodes, die alle zwei Byte lang sind. Das höchstwertige Byte wird zuerst gespeichert. Die Opcodes sind unten in hexadezimal und mit den folgenden Symbolen aufgelistet:
Hex | Symbolisch | Assembler | Beschreibung |
---|---|---|---|
1mmm | GO mmm | JP addr | Go to 0MMM |
Bmmm | GO mmm+V0 | JP V0, addr | Go to 0MMM + V0 |
2mmm | DO mmm | CALL addr | Do subroutine at 0MMM (must end with 00EE) |
00EE | RET | RET | Return from subroutine |
3xkk | SKIP;Vx EQ kk | SE Vx, byte | Skip next instruction if VX = KK |
4xkk | SKIP;Vx NE kk | SNE Vx, byte | Skip next instruction if VX <> KK |
5xy0 | SKIP;Vx EQ Vy | SE Vx, Vy | Skip next instruction if VX = VY |
9xy0 | SKIP;Vx NE Vy | SNE Vx, Vy | Skip next instruction if VX <> VY |
Ex9E | SKIP;Vx EQ KEY | SKP Vx | Skip next instruction if VX = Hex key (LSD) |
ExA1 | SKIP;Vx NE KEY | SKNP Vx | Skip next instruction if VX <> Hex key (LSD) |
6xkk | Vx=kk | LD Vx, byte | Let VX = KK |
Cxkk | Vx=RND | RND Vx, byte | Let VX = Random Byte (KK = Mask) |
7xkk | Vx=Vx+kk | ADD Vx, byte | Let VX = VX + KK |
8xy0 | Vx=Vy | LD Vx, Vy | Let VX = VY |
8xy1 | Vx=Vx/Vy | OR Vx, Vy | Let VX = VX / VY (VF changed) |
8xy2 | Vx=Vx&Vy | AND Vx, Vy | Let VX = VX & VY (VF changed) |
8xy4 | Vx=Vx+Vy | ADD Vx, Vy | Let VX = VX + VY (VF = 00 if VX + VY ⇐ FF, VF = 01 if VX + VY > FF) |
8xy5 | Vx=Vx-Vy | SUB Vx, Vy | Let VX = VX - VY (VF = 00 if VX < VY, VF = 01 if VX >= VY) |
Fx07 | Vx=TIME | LD Vx, DT | Let VX = current timer value |
Fx0A | Vx=KEY | LD Vx, K | Let VX = hex key digit (waits for any key pressed) |
Fx15 | TIME=Vx | LD DT, Vx | Set timer = VX (01 = 1/60 second) |
Fx18 | SND=Vx | LD ST, Vx | Set tone duration = VX (01 = 1/60 second) |
Ammm | I=mmm | LD I, addr | Let I = 0MMM |
Fx1E | I=I+Vx | ADD I, Vx | Let I = I + VX |
Fx29 | I=Vx(LSDP) | LD F, Vx | Let I = 5-byte display pattern for LSD of VX |
Fx33 | MI=Vx(3DD) | LD B, Vx | Let MI = 3-decimal digit equivalent of VX (I unchanged) |
Fx55 | MI=V0:Vx | LD [I], Vx | Let MI = V0 : VX (I = I + X + 1) |
Fx65 | V0:Vx=MI | LD Vx, [I] | Let V0 : VX = MI (I = I + X + 1) |
00E0 | ERASE | CLS | Erase display (all 0's) |
DxyN | SHOW nMI@VxVy | DRW Vx, Vy, nibble | Show n-byte MI pattern at VX-VY coordinates. I unchanged. MI pattern is combined with existing display via EXCLUSIVE-OR function. VF = 01 if a 1 in MI pattern matches 1 in existing display. |
0mmm | MLS@mmm | SYS addr | Do 1802 machine language subroutine at 0MMM (subroutine must end with D4 byte) |
Die Assemblerbezeichungen entsprechen http://devernay.free.fr/hacks/chip8/C8TECH10.HTM
; Tank, from VIPER vol 1, issue 1 june 1978, pp. 14
200 6120 init: V1=20 ;initialize 202 6210 V2=10 204 A240 I=240 206 D127 show: SHOW 7MI@V1V2 ;show tank 208 6002 key: V0=2 ;key wait 20A E0A1 key1: SKIP;V0 NE KEY 20C 1216 GO erase ; 216 20E 7002 V0+2 210 300A SKIP;V0 EQ 0A 212 120A GO key ; 20A ;loop back to check next key 214 1208 GO key1 ; 208 ;loop back to recheck next key 216 D127 erase: SHOW 7MI@V1V2 ;erase tank 218 4002 SKIP;V0 NE 02 ;change x or y 21A 72FF V2+=FF ; = V2-1; move up 21C 4004 SKIP;V0 NE 04 21E 71FF V1+=FF ; = V1-1; move left 220 4006 SKIP;V0 NE 06 222 7101 V1+=01 ; move right 224 4008 SKIP;V0 NE 08 226 7201 V2+=01 ; move down 228 4002 SKIP;V0 NE 02 ; set pointer 22A A240 I=s_up ;240 22C 4004 SKIP;V0 NE 04 22E A253 I=s_left ;253 230 4006 SKIP;V0 NE 06 232 A24D I=s_right ;24D 234 4008 SKIP;V0 NE 08 236 A246 I=s_down ;246 238 1206 GO show ;206 ; jump to show ; Sprites 240 10 s_up: db 00010000b ; ...#.... 241 54 db 01010100b ; .#.#.#.. 242 7C db 01111100b ; .#####.. 243 6C db 01101100b ; .##.##.. 244 7C db 01111100b ; .#####.. 245 7C db 01111100b ; .#####.. 246 ; 246 44 s_down: db 01000100b ; .#...#.. 247 7C db 01111100b ; .#####.. 248 7C db 01111100b ; .#####.. 249 6C db 01101100b ; .##.##.. 24A 7C db 01111100b ; .#####.. 24B 54 db 01010100b ; .#.#.#.. 24C 10 db 00010000b ; ...#.... 24D ; 24D 00 s_right: db 00000000b ; ........ 24E FC db 11111100b ; ######.. 24F 78 db 01111000b ; .####... 250 6E db 01101110b ; .##.###. 251 78 db 01111000b ; .####... 252 FC db 11111100b ; ######.. 253 ; 253 00 s_left: db 00000000b ; ........ 254 3F db 00111111b ; ..###### 255 1E db 00011110b ; ...####. 256 76 db 01110110b ; .###.##. 257 1E db 00011110b ; ...####. 258 3F db 00111111b ; ..###### 259 00 db 00000000b ; ........ 25A END
Der CHIP-8-Interpreter eignet sich aufgrund seiner einfachen Befehlssatzes ideal als Einstiegsobjekt in das Programmieren von Emulatoren. Es gibt mehrere Seiten im Netz, die das schrittweise vormachen.
Und es gibt diverse Emulatoren, z.B. den VISION von M.Kogel.
EMMA 02 emuliert den COSMAC VIP
VISION8 ist ein CHIP-8-Emulator
CHIPPER V2.11 ist ein Assembler für Chip-8.
Beim KC-Club Treffen 2013 habe ich einen Vortrag über die Programmiersprache CHIP-8 und die Implementation eines CHIP-8-Interpreters für den Z9001 gehalten.