/vice.js

Versatile Commodore Emulator for JavaScript

Primary LanguageShell

VICE.js

Versatile Commodore Emulator for JavaScript

JavaScript port of VICE 2.4 using Emscripten.

Demo
VICE
Emscripten

Browser Version Status Note
Firefox 39 :) ok
Chrome 43 :
IE 11 :( broken

Examples

###c64 basic

<!doctype html>
<html lang="en-us">
    <body>
        <!-- the canvas *must not* have any border or padding, or mouse coords will be wrong -->
        <canvas  id="canvas" style="border: 0px none;"></canvas>
        <script type="text/javascript" >
            var Module = {
                arguments: ['+sound'],
                canvas: document.getElementById('canvas'),
            };
        </script>
        <script type="text/javascript" src="js/x64.js"></script>
    </body>
</html>

###auto-run a D64 disk image

<!doctype html>
<html lang="en-us">
    <body>
        <!-- the canvas *must not* have any border or padding, or mouse coords will be wrong -->
        <canvas  id="canvas" style="border: 0px none;"></canvas>
        <script type="text/javascript" >

            function audioDetected() {
                return (typeof Audio === 'function' && new Audio().mozSetup === 'function') ||
                    (typeof AudioContext === 'function') ||
                    (typeof webkitAudioContext === 'function');
            }

            function loadFiles() {
                var base64EncodedFile = '...';
                FS.createDataFile('/', 'disk-image.d64', window.atob(base64EncodedFile), true, true);
            }

            var viceArguments = ['-autostart', 'disk-image.d64'].concat(
                audioDetected() ? ['-soundsync', 0, '-soundrate', 22050, '-soundfragsize', 2] : ['-sound']
            );

            var Module = {
                preRun: [loadFiles],
                arguments: viceArguments,
                canvas: document.getElementById('canvas'),
            };

        </script>
        <script type="text/javascript" src="js/x64.js"></script>
    </body>
</html>

###send keyboard commands

<!doctype html>
<html lang="en-us">
    <body>
        <!-- the canvas *must not* have any border or padding, or mouse coords will be wrong -->
        <canvas  id="canvas" style="border: 0px none;"></canvas>
        <p><button onmousedown="sendSpaceKeyPressedCode()" onmouseup="sendSpaceKeyReleasedCode()">space bar</button></p>
        <script type="text/javascript" >

            var C64_SPACE_KEY_CODE = 32;

            function sendSpaceKeyPressedCode() {
                Module.ccall('keyboard_key_pressed', 'undefined', ['number'], [C64_SPACE_KEY_CODE]);  
            }

            function sendSpaceKeyReleasedCode() {
                Module.ccall('keyboard_key_released', 'undefined', ['number'], [C64_SPACE_KEY_CODE]);
            }

            var Module = {
                arguments: ['+sound'],
                canvas: document.getElementById('canvas'),
            };
            
        </script>
        <script type="text/javascript" src="js/x64.js"></script>
    </body>
</html>

Best Configuration Options

async mode:

  • soundfragsize 2 -soundrate 22050 -soundsync 0

sync mode:

  • soundfragsize 2 -soundrate 22050 -soundsync 0 -ntsc
  • ntsc is important because browser requestAnimationFame is going to deliver 60 fps which means less cpu time is wasted during vsync delay

Development

###How to find key codes

  • in /vice/src/arch/sdl/kbd.c # sdlkbd_press()
  • this line prints key codes to console when key is pressed
fprintf(stderr, "%s: %i (%s),%i\n",__func__,key,SDL_GetKeyName(key),mod);
  • activate by compiling vice with SDL_DEBUG flag

Resources

C64 Wiki
Commodore 64 keyboard matrix layout
COMMODORE 64 ROGRAMMER'S REFERENCE GUIDE
Commodore Manuals
Coroutines in C
requestAnimationFrame setting fps
SDL 1.2 to 2.0 Migration Guide
VICE Manual
VICE options

Tasks

  • try using worker thread (emcc --proxy-to-worker)
    • initial tests show +10% perf in Chrome, but missing SDL Audio
    • discuss proxy-to-worker
    • discuss web audio & web workers
    • nice callback pattern for web workers
    • Proxy SDL Audio
      • \vice\src\sounddrv\soundsdl.c
      • \emscripten\src\library_sdl.js
        • line 1636 creates Audio object
      • \emscripten\src\proxyWorker.js
      • \emscripten\src\proxyClient.js
      • Native audio classes shim proxy option: Create proxies for each Audio class and simply define these classes in proxyWorker.js.
        • proxyClient will feature detect available Audio classes and send this info the proxyWorker
        • proxyWorker will implement Audio classes based on features detected by proxyClient
        • Conclusions
          • A large API needs to be proxied, lots of different Classes some vary by browser.
          • Managing all the audio object proxies (createBufferSource, createBuffer) is complicated.
          • Need to track references to proxied objects.
          • Determining when to free objects can be difficult.
          • postMessage communication would be chatty.
      • Proxy SDL_*Audio C functions
        • Seperate C code from Web Audio API's
          • Re-factor pushAudio function to use data arrays instead of a pointer to heap
        • Copy Web Audio parts of SDL_*Audio functions to proxyClient
        • Detect proxyClient in library_sdl and invoke proxy versions of SDL_*Audio functions
        • Conclusions
          • Code duplication, some code from SDL_*Audio functions exist in both library_sdl and proxyClient
          • extra data copy required by pushAudio function
            • current process
              • copy data from heap directly into Web Audio buffers $expensive
            • new worker process
              • copy data from heap into ArrayBuffer $expensive
              • transfer ownership of data to browser thread
              • copy data into Web Audio buffers $expensive
        • Issues
          • SDL init is synchronous so if there's a problem in the browser context part, worker does not know.
            • Possible solution is to feature detect Web Audio and send the info to worker ahead of time so it can make a better SDL init process.
          • Sound buffer overflows if tab goes to background, probably caused by request animation frame slowing down browser thread but worker is still at full speed.
          • Sound clicks, weird timing
        • Use function addRunDependency(id) instead of custom waitForAudioFeatures logic.
        • Test with \emscripten\tests\sdl_audio_beep.cpp
          • ./emcc -O2 --minify 0 -s DISABLE_EXCEPTION_CATCHING=0 -o audio-beep.html --proxy-to-worker --closure 1 tests/sdl_audio_beep.cpp
        • proxy-dependency-bug
          • ./emcc tests/hello_world_sdl.cpp -o hello.html --proxy-to-worker --post-js hello_world_dependencies.js
  • file issue for Emscripten with patch
    • Chrome has a console in web worker context so detect it before over-writing @ proxyWorker.js line ~81
self.console = self.console || {
  log: function(x) {
    Module.printErr(x);
  }
};
  • add other computers
    • VIC-20
  • fix vice menu ui (F12)