Übersetzen alter Arcade-Spiele in JavaScript

810 Aufrufe

Veröffentlicht am

Folien zum Vortrag auf dem VCFE 2015. Es wird gezeigt, wie klassische 6502-basierende Videospiele automatisch in JavaScript übersetzt werden können. Der Übersetzungsprozess und die Optimierungsmöglichkeiten sind überraschend herausfordernd. Nach dem Hinzufügen der spezifischen Grafikausgabe erhält man in modernen Browsern ausführbare originalgetreue Videospiele.

Veröffentlicht in: Software
0 Kommentare
0 Gefällt mir
Statistik
Notizen
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

Keine Downloads
Aufrufe
Aufrufe insgesamt
810
Auf SlideShare
0
Aus Einbettungen
0
Anzahl an Einbettungen
3
Aktionen
Geteilt
0
Downloads
2
Kommentare
0
Gefällt mir
0
Einbettungen 0
Keine Einbettungen

Keine Notizen für die Folie

Übersetzen alter Arcade-Spiele in JavaScript

  1. 1. Übersetzen alter Arcade-Spiele in JavaScript Norbert Kehrer VCFe, München, 1. Mai 2015
  2. 2. Automatisches Übersetzen klassischer Spiele ist spannend  Ziel: Originalgetreue Umsetzung klassischer Videospiele z.B. im Browser  Der traditionelle Weg: Emulatoren  Eine interessante Alternative: Statische Binärcodeübersetzung (Static Binary Translation, Static Recompilation)  Beispiele: Asteroids (1979), Centipede (1980), Astro Fighter (1980)
  3. 3. Was erwartet euch?  Emulieren vs. Rekompilieren am Beispiel „Asteroids“  Die Quelle: der 6502  Das Ziel: JavaScript  „Naive“ Übersetzungsmuster  Code-Optimierung und der aufregende Weg bis tief in die Compilerbautheorie
  4. 4. 6502 in JavaScript übersetzen  Emulieren vs. Rekompilieren am Beispiel „Asteroids“  Die Quelle: der 6502  Das Ziel: JavaScript  „Naive“ Übersetzungsmuster  Code-Optimierung und der aufregende Weg bis tief in die Compilerbautheorie
  5. 5. Der Asteroids-Arcade-Automat basiert auf dem 6502-Prozessor Program ROM 6800–7fff Work RAM 0000–02ff Vector ROM 5000–57ff Vector RAM 4000–47ff 6502 DVG Spielelogik Videohardware
  6. 6. Klassisches Emulieren der Hardware ... Program ROM 6800–7fff Work RAM 0000–02ff Vector ROM 5000–57ff Vector RAM 4000–47ff 6502 DVG Emulator in JavaScript
  7. 7. ... oder das Spielprogramm in JavaScript übersetzen, … Program ROM 6800–7fff Work RAM 0000–02ff Vector ROM 5000–57ff Vector RAM 4000–47ff 6502 DVG JavaScript Emulator in JavaScript
  8. 8. … und so eine Stand-Alone- Applikation schaffen Program ROM 6800–7fff Work RAM 0000–02ff Vector ROM 5000–57ff Vector RAM 4000–47ff 6502 DVG JavaScript Emulator in JavaScript
  9. 9. 6502 in JavaScript übersetzen  Emulieren vs. Rekompilieren am Beispiel „Asteroids“  Die Quelle: der 6502  Das Ziel: JavaScript  „Naive“ Übersetzungsmuster  Code-Optimierung und der aufregende Weg bis tief in die Compiler-Theorie
  10. 10. Der 6502 war in den 1980-ern ein beliebter Mikroprozessor
  11. 11. Der 6502 ist einfach und hat wenige Register Akkumulator (A) X-Register (X) Y-Register (Y) Programmzähler (PC) Flags: NV-BDIZC 00000001 Stack-Pointer (S) 0000-00ff: Zero-Page 0100-01ff: Stack 0200-ffff: Programm Register Speicher
  12. 12. Der 6502 hat 3 Befehlsformate Op-Code Op-Code Wert o. ZP-Adr. Op-Code Adresse Low Adresse High $a9 $03 … lda #3 $8d $07 $19 … sta $1907 $0a … asl
  13. 13. Einige Befehlsbeispiele  lda #1 Lade Akku mit 1  sta 1000 Speichere A. in 1000  inx Erhöhe X-Reg. um 1  adc #7 Addiere 7 zum Akku  jmp 3000 Springe nach 3000  beq 2000 Bedingter Sprung, wenn Z-Flag == 1
  14. 14. 6502 in JavaScript übersetzen  Emulieren vs. Rekompilieren am Beispiel „Asteroids“  Die Quelle: der 6502  Das Ziel: JavaScript  „Naive“ Übersetzungsmuster  Code-Optimierung und der aufregende Weg bis tief in die Compilerbautheorie
  15. 15. JavaScript ist ein C-Derivat  Wichtigste Skriptsprache für Browser  Fast die gleiche Syntax wie C und Java  Variablen: var x;  Arrays: var a = new Array(100);  Zuweisungen: n = a[19]; k = 7;  Arithmetik: x = x + 5 * a[4];
  16. 16. Bedingte Anweisungen und Auswahlanweisung wie in C  if (bed) { anw1 } else { anw2 };  switch (var) { case a: anw1; break; case b: anw2; break; case c: anw3; break; … };
  17. 17. 6502 in JavaScript übersetzen  Emulieren vs. Rekompilieren am Beispiel „Asteroids“  Die Quelle: der 6502  Das Ziel: JavaScript  „Naive“ Übersetzungsmuster  Code-Optimierung und der aufregende Weg bis tief in die Compilerbautheorie
  18. 18. Zuerst einen Disassembler schreiben … … 6834: A2 44 ldx #$44 6836: A9 02 lda #$02 6838: 85 02 sta $02 683A: 86 03 stx $03 … … A2 44 A9 02 85 02 86 03 … Programm-ROM (aus Asteroids) Disassembler-Listing
  19. 19. … und daraus den Code- Generator machen … … ldx #$44 x = 0x44; … lda #$02 a = 2; … sta $02 mem[2] = a; … stx $03 mem[3] = x; … … A2 44 A9 02 85 02 86 03 … Programm-ROM (aus Asteroids) JavaScript-Code statt Disassembler-Listing
  20. 20. 6502-Register und Speicher werden zu Variablen und Arrays Akkumulator X-Register Y-Register C-Flag N-Flag … Speicher var a; var x; var y; var c; var n; var mem = new Array(65536);
  21. 21. „Normale“ Befehle sind einfach übertragbar, ... lda 1000 sta 1001 inc 1000 ldx #10 sta 2000,x inx a = mem[1000]; mem[1001] = a; mem[1000] = (mem[1000]+1)&0xff; x = 10; mem[2000+x] = a; x = (x+1) & 0xff;
  22. 22. ... but „GOTO Considered Harmful“ is considered harmful … 1000: ldx #0 ; x = 0 1002: inx ; x = x + 1 1003: stx $d021 ; x  Schirmfarbe 1006: jmp 1002 ; springe nach 1002 … Aber JavaScript hat kein (richtiges) GOTO !
  23. 23. Ein alter „Fortran-to-C“-Trick pc = 1000; while (true) { switch (pc) { case 1000: x = 0; //ldx case 1002: x = (x+1) & 0xff; //inx case 1003: mem[0xd021] = x; //stx case 1006: pc = 1002; break; //jmp … }; };
  24. 24. Case-Labels braucht man nur für Sprungziele pc = 1000; while (true) { switch (pc) { case 1000: x = 0; //ldx case 1002: x = (x+1) & 0xff; //inx case 1003: mem[0xd021] = x; //stx case 1006: pc = 1002; break; //jmp … }; };
  25. 25. Bedingte “Branches” werden zu If-Anweisungen … if (z == 1) { // beq 3000 pc = 3000; break; }; … case 3000: …
  26. 26. Dr. Sheldon Cooper‘s „Fun with Flags“  Als Nebeneffekt vieler 6502-Befehle werden Flags gesetzt  Beispiel: lda 1000 wenn null  Z=1 sonst Z=0 wenn neg.  N=1 sonst N=0 beq 4711 …
  27. 27. Viele Befehle brauchen daher auch Flag-Berechnungscode lda 1000 lda 1000 a = mem[1000]; a = mem[1000]; if (a==0) z=1; else z=0; if (a<0) n=1; else n=0;  Korrekte, aber sehr große Kompilate
  28. 28. 6502 in JavaScript übersetzen  Emulieren vs. Rekompilieren am Beispiel „Asteroids“  Die Quelle: der 6502  Das Ziel: JavaScript  „Naive“ Übersetzungsmuster  Code-Optimierung und der aufregende Weg bis tief in die Compilerbautheorie
  29. 29. Flag-Berechnungen sind sehr oft überflüssig … lda 1000 a = mem[1000]; if (a==0) z=1; else z=0; if (a<0) n=1; else n=0; ldx 1200 x = mem[1200]; if (x==0) z=1; else z=0; if (x<0) n=1; else n=0; beq 4711 if (z==1){ pc=4711; break; };
  30. 30. Flag-Berechnungen sind sehr oft überflüssig … lda 1000 a = mem[1000]; if (a==0) z=1; else z=0; if (a<0) n=1; else n=0; ldx 1200 x = mem[1200]; if (x==0) z=1; else z=0; if (x<0) n=1; else n=0; beq 4711 if (z==1) ){ pc=4711; break; };
  31. 31. ?: OK: … aber nicht immer lda 1000 a = mem[1000]; if (a==0) z=1; else z=0; if (a<0) n=1; else n=0; ldx 1200 x = mem[1200]; if (x==0) z=1; else z=0; if (x<0) n=1; else n=0; beq 4711 if (z==1) ){ pc=4711; break; };
  32. 32. Nur, wenn später gebraucht und nicht bis dahin neu berechnet  „Später“  Berechnung des Kontrollflussgraphen  Zyklen im Graphen  Use-Def-Mengen
  33. 33. Dead-Code-Elimination ist schwierig und interessant  Liveness-Analysis-Problem  Lösung über Fixpunktiteration oder eleganter mit Datalog-Programmen  Viele spannende Wege führen weiter: Weitere Optimierungen Erkennen von Strukturen (while, if, ...) LLVM logische Programmierung, …
  34. 34. Zusammenfassung: Von Asteroids zur Liveness-Analysis  Browser-„Asteroids“ als Beispiel  6502-Prozessor  JavaScript  Übersetzung von 6502 in JavaScript  Optimierung: Dead-Code-Elimination
  35. 35. Danke ! http://members.aon.at/nkehrer/

×