Giter Site home page Giter Site logo

eboy's Introduction

Eboy - Emacs Gameboy Emulator

A Nintendo Gameboy emulator for Emacs. This is a work in progress! Just got Tetris starting up, not tested with anything else.

It currently runs a constant loop without delay. To get out of this loop, use the common C-g command.

The display is drawn using unicode, by default it is set for dark theme. Use the unicode list below when you have a light theme installed in Emacs.

(setq eboy-display-unicode-list eboy-display-unicode-list-light-theme)

It has frameskip enabled, by default set to 20.

Why?

I always wanted to learn how to a make an emulator and want learn how to write packages for Emacs. So here my first emulator and first emacs package.

Prerequisites

Emacs :)

Installing

For now just M-x eval-buffer or M-x byte-compile-file the following files: eboy-macros.el, eboy-cpu.el and eboy.el.

Usage

Load a rom using the M-x eboy-load-rom command.

Gameboy Eboy
Start Enter
Select Space
B D
A S
down k
up i
left j
right l

To continue a game after you pressed C-g, use the M-x eboy-run command.

Eboy related messages are written to the buffer *Eboy Messages*.

Blargg's test roms status

  1. special: Seems to hang, without giving an error
  2. interrupts: Passed
  3. op hl,sp: F8 F8 Failed
  4. op r,imm: C6 DE Failed
  5. op rp: Passed
  6. ld r,r: Passed
  7. jr,jp,call,ret,rst: Seems to hang, without giving an error
  8. misc instrs: Passed
  9. op r,r: List of failing opcodes 3F, 07, 17, 0F, CB 00, CB 01, CB 02, CB 03, CB 05, CB 07, Cb 08, CB 09, CB 0A, CB 0B, CB 0C, CB 0D, CB 0F, CB 10, CB 11, CB 12, CB 13, CB 14, CB 15, CB 17, CB 20, CB 21, CB 22, CB 23, CB 24, CB 25, CB 27,
  10. bit ops: Passed
  11. op a,(hl): List of failing opcodes CB 0E,CB 2E,CB 3E,CB 46,CB 4E,CB 56,CB 5E,CB 66,CB 6E,CB 76,CB 7E,CB 86,CB 8E,CB 96,CB 9E,CB A6,CB AE,CB B6,CB BE,CB C6,CB CE,CB D6,CB DE,CB E6,CB EE,CB F6,CB FE

The test roms are hosted at: https://github.com/retrio/gb-test-roms

eboy's People

Contributors

vreeze avatar wasamasa avatar

Stargazers

 avatar  avatar Arch 💕 avatar Kyure_A avatar Adonis avatar DMz avatar alaska avatar sfome avatar Vladimir Berezhnev avatar kevin avatar hussein-aitlahcen avatar Ben Hunt avatar Colin Woodbury avatar Michael Pope avatar  avatar Oleg Pykhalov avatar Anderson Torres avatar  avatar Lucas Vieira avatar Jamie Sparks avatar Andrejs Agejevs avatar Alex Kehayias avatar Zachary Pigott avatar  avatar Josh Mize avatar Karl Voit avatar S Knutsen avatar auslegungssache avatar Barak Karavani avatar Jordan Brown avatar Prasant Acharya avatar  avatar Benjamin Levy avatar Conscat avatar Luis A Rivera Sanabria avatar Corwin avatar Andrew Foltz-Morrison avatar bartuka avatar Exceptionz Project avatar shakenetwork avatar Ben Konz avatar  avatar  avatar Geekinney avatar 20chan avatar 我没有抓狂 avatar 李无常 avatar  avatar  avatar Jade Michael Thornton avatar Joseph Bauer avatar Miguel SM avatar Remi Rousselet avatar Rodrigo Muniz avatar Jeremy Kahn avatar Aaron Turner avatar Henrik Kjerringvåg avatar Sean Grove avatar Ammar K. avatar Bence Cs avatar Jacob Motley avatar  avatar kawabata avatar Yaroslav Shalenyk avatar Fred Campos avatar Kentaro Ohkouchi avatar GAURAV avatar Kosti avatar Marco Lee avatar Samarth Kishor avatar Anselm Joseph avatar SteamedFish avatar liuxueyang avatar  avatar  avatar Slava Shirokov avatar Phi avatar Uffe Jakobsen avatar sleetdrop avatar Arnstein Henriksen avatar James Sral avatar  avatar chan2565 avatar Matthew A. Clarke avatar Amory Meltzer avatar Pranav Maddula avatar Saeed Gholami Shahbandi avatar Vasantha Ganesh Kanniappan avatar Thomas Louvigné avatar  avatar Erik Humphrey avatar  avatar Andreas Wilfer avatar tumashu avatar Christian W. avatar Jeff Patyk avatar Fernando avatar Guilherme Guerra avatar Dmitry Akatov avatar Zeljko Stevanovic avatar

Watchers

Black avatar 66tr.ee avatar  avatar Russell Davis avatar Panji Kusuma avatar Jerven Clark Chua avatar  avatar

eboy's Issues

Failed to load rom

Tried to load http://lgarc.narod.ru/MortalKombatU.gb rom with M-x eboy-load-rom RET and got the backtrace:

Debugger entered--Lisp error: (args-out-of-range [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...] -2)
  aset([0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...] -2 57)
  (cond ((= address 65535) (setq eboy-interrupt-enabled data)) ((and (>= address 65408) (<= address 65535)) (aset eboy-hram (- address 65408) data)) ((and (>= address 65356) (< address 65408)) (aset eboy-ram (- address 57344) data)) ((and (>= address 65280) (< address 65356)) (cond ((= address 65355)) ((= address 65355)) ((= address 65354)) ((= address 65360) (setq eboy-rom (concat eboy-low-rom (seq-drop eboy-rom (length eboy-low-rom))))) ((= address 65353)) ((= address 65352)) ((= address 65351)) ((= address 65350) (let ((byte_nr 0) (base_address (lsh data 8))) (while (< byte_nr 160) (eboy-mem-write-byte (+ 65024 byte_nr) (eboy-mem-read-byte ...)) (setq byte_nr (1+ byte_nr))))) ((= address 65349)) ((= address 65348) (setq eboy-lcd-ly 0)) ((= address 65347) (setq eboy-lcd-scrollx data)) ((= address 65346) (setq eboy-lcd-scrolly data)) ((= address 65345)) ((= address 65344) (setq eboy-lcdc-display-enable (= (logand data 128) 128)) (setq eboy-lcdc-window-tile-map-select (= (logand data 64) 64)) (setq eboy-lcdc-window-display-enable (= (logand data 32) 32)) (setq eboy-lcdc-bg-&-window-tile-data-select (= (logand data 16) 16)) (setq eboy-lcdc-bg-tile-map-display-select (= (logand data 8) 8)) (setq eboy-lcdc-obj-size (= (logand data 4) 4)) (setq eboy-lcdc-obj-disp-on (= (logand data 2) 2)) (setq eboy-lcdc-bg-window-on (= (logand data 1) 1))) ((= address 65295) (setq eboy-interrupt-pending data)) ((= address 65287)) ((= address 65286)) ((= address 65285)) ((= address 65284)) ((= address 65282)) ((= address 65281)) ((= address 65280))) (aset eboy-io (- address 65280) data)) ((and (>= address 65184) (< address 65280)) (aset eboy-ram (- address 57344) data)) ((and (>= address 65024) (< address 65184)) (aset eboy-ram (- address 57344) data)) ((and (>= address 57344) (< address 65024)) (aset eboy-ram (- address 57344) data)) ((and (>= address 49152) (< address 57344)) (aset eboy-ram (- address 49152) data)) ((and (>= address 40960) (< address 49152)) (aset eboy-sram (- address 49152) data)) ((and (>= address 32768) (< address 40960)) (aset eboy-vram (- address 32768) data)) ((and (>= address 16384) (< address 32768))) ((and (>= address 0) (< address 16384))))
  eboy-mem-write-byte(49150 57)
  eboy-mem-write-short(49150 57)
  (closure (t) nil "0xFF RST 38H  " (setq eboy-sp (- eboy-sp 2)) (eboy-mem-write-short eboy-sp (+ eboy-pc 1)) (setq eboy-pc (1- 56)) (setq eboy-clock-cycles (+ eboy-clock-cycles 16)))()
  funcall((closure (t) nil "0xFF RST 38H  " (setq eboy-sp (- eboy-sp 2)) (eboy-mem-write-short eboy-sp (+ eboy-pc 1)) (setq eboy-pc (1- 56)) (setq eboy-clock-cycles (+ eboy-clock-cycles 16))))
  (if eboy-cpu-halted (eboy-inc-pc 1) (funcall (aref eboy-cpu opcode)) (eboy-inc-pc 1))
  eboy-process-opcode(255)
  (while t (eboy-process-opcode (eboy-mem-read-byte eboy-pc)) (eboy-process-interrupts) (eboy-lcd-cycle) (eboy-timer-cycle))
  eboy-run()
  eboy-load-rom("~/.emacs.d/eboy-roms/MortalKombatU.gb")
  funcall-interactively(eboy-load-rom "~/.emacs.d/eboy-roms/MortalKombatU.gb")
  call-interactively(eboy-load-rom record nil)
  command-execute(eboy-load-rom record)
  execute-extended-command(nil "eboy-load-rom" nil)
  funcall-interactively(execute-extended-command nil "eboy-load-rom" nil)
  call-interactively(execute-extended-command nil nil)
  command-execute(execute-extended-command)

I'm using GNU Emacs 27.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.18.9) of 2018-11-13

Performance

Yo! This project is awesome. I don't know much about hardware or
low-level programming in general, so I'm excited to find out how the
whole thing works.

As I'm sure you're aware, performance is an issue at the moment. On my
crappy little Thinkpad, it takes several minutes to go from loading
Tetris to the second piece appearing during gameplay.

So I profiled for CPU time, running the interpreted code (commit
937e63c) from loading until the second Tetris piece appears. Here are
the results, with irrelevant stuff cut out:

- eboy-load-rom                                     51%
 - eboy-run                                         51%
  - while                                           50%
   - eboy-process-opcode                            25%
    - if                                            20%
     + funcall                                      18%
     + eboy-inc-pc                                   1%
    + eboy-mem-read-byte                             2%
   - eboy-lcd-cycle                                 17%
    - let                                           16%
     - if                                           14%
      - progn                                       13%
       - eboy-debug-update-fps                      13%
        - if                                        13%
         - progn                                    13%
          - eboy-write-display-unicode              13%
           - let                                     9%
            + let                                    9%
           + redisplay                               3%
          + eboy-read-keys                           0%
   + eboy-process-interrupts                         3%
   + eboy-timer-cycle                                2%
Automatic GC                                        48%

48% of CPU time spent in garbage collection, ouch! That means that too
much garbage is being created. Cutting down on that should have a
significant impact on performance.

So I profiled for memory.

- eboy-load-rom                                3,748,704,464  99%
 - eboy-run                                    3,748,351,560  99%
  - while                                      3,748,350,536  99%
   - eboy-process-opcode                       2,085,125,256  55%
    - if                                       1,834,769,803  48%
     - funcall                                 1,584,632,340  41%
      + #<lambda 0x129bd1ce6ac1ba49>             872,248,406  23%
      + #<lambda -0xb04ddad4bebffdd>             291,892,213   7%
      + #<lambda 0x1627d4710ba0e62d>             228,615,133   6%
      + #<lambda -0x4d5d1759851bda8>              38,206,101   1%
   - eboy-lcd-cycle                              900,930,468  23%
    - let                                        651,168,145  17%
     - if                                        651,072,911  17%
      - progn                                    651,072,911  17%
       - eboy-debug-update-fps                   651,072,911  17%
        - if                                     651,072,911  17%
         - progn                                 651,004,271  17%
          - eboy-write-display-unicode           650,744,042  17%
           - let                                 642,287,662  16%
            - let                                642,286,606  16%
             - while                             642,286,606  16%
              - let                              642,023,662  16%
               - let                             629,355,236  16%
                - while                          613,176,550  16%
                 - let                           576,671,503  15%
                  - setq                         576,636,407  15%
                   - nth                         540,956,279  14%
                    + eboy-get-color-xy          469,317,047  12%
   + eboy-timer-cycle                            259,579,949   6%
   + eboy-process-interrupts                     250,632,361   6%

Those numbers are for the interpreted code. Byte compiling makes a big
difference. Here is the memory profile for the byte compiled code
running all the way until the Tetris pieces reach the top of the
screen:

- eboy-load-rom                                  151,831,860  91%
 - eboy-run                                      151,489,484  90%
  - eboy-lcd-cycle                               151,485,348  90%
   - eboy-debug-update-fps                       151,485,348  90%
    - eboy-write-display-unicode                  45,211,847  27%
     + redisplay                                   3,058,555   1%
       mod                                            66,856   0%
       eboy-display-sprites                           65,472   0%
     + eboy-get-color-xy                                 512   0%
    + read-char                                       61,744   0%
  + eboy-process-opcode                                4,136   0%

And the CPU profile for the byte compiled code:

- eboy-load-rom                                     96%
 - eboy-run                                         92%
  - eboy-process-opcode                             35%
   + #<compiled 0x158338c1783d>                      8%
   + #<compiled 0x158337784dcd>                      6%
   + #<compiled 0x15833848ecc5>                      5%
  - eboy-lcd-cycle                                  32%
   - eboy-debug-update-fps                          31%
    - eboy-write-display-unicode                    29%
     + redisplay                                    15%
     - eboy-get-color-xy                            11%
      + eboy-get-color                               2%
        eboy-get-tile-id                             2%
  + eboy-process-interrupts                          1%
Automatic GC                                         3%

Based on this data, I have some ideas on how to improve some critical
sections:

  1. The CPU is currently vector filled with argument-less lambdas. This
    could be replaced with a big case switch statement. That would be
    uglier, but I think it would consume less memory.

  2. eboy-write-display-unicode contains this section:

    (dotimes (y 144)
      (setq line nil)
      (dotimes (x 160)
        (setq c (nth (eboy-get-color-xy (mod (+ x eboy-lcd-scrollx) 256) (mod (+ y eboy-lcd-scrolly) 256)) eboy-display-unicode-list))
        (setq line (cons c line))
        (setq line (cons c line))) ;; draw x twice, to get a more square sized display.
      (setq line (nreverse line))

Multiple conses are made in a very tight loop, and this consumes a lot
of memory. This would more efficient with vectors instead of lists.

I hope this is useful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.