/*------------------------------------------------------------------------- | SAE - Scripted Amiga Emulator | https://github.com/naTmeg/ScriptedAmigaEmulator | | Copyright (C) 2012-2016 Rupert Hausberger | | This program is free software; you can redistribute it and/or | modify it under the terms of the GNU General Public License | as published by the Free Software Foundation; either version 2 | of the License, or (at your option) any later version. | | This program is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | Notes on the global-namespace | ----------------------------- | Global object: | function SAEO_() {} | | Global error constant: | const SAEE__; | | Global constant: | const SAEC__; | | Global variable: | var SAEV__; | | Global function: | function SAEF__() {} | | Global reference (pointer) to another object: | var SAER__; | | | Tags that may appear in comments (use full-word, case-sensitive text-search) | -------------------------------- | OWN Own code added. | ATT Attention, possible source of error or problem. | FIX Need to be fixed or implemented. | OPT Need or can be optimized. | ORG Original code, e.g. something required to be disabled. | SECT Code section. -------------------------------------------------------------------------*/ const SAEC_Version = 0; const SAEC_Revision = 9; const SAEC_Patch = 0; /*---------------------------------*/ /* errors */ function SAEO_Error(err, msg) { this.err = err; this.msg = msg; } SAEO_Error.prototype = new Error; const SAEE_None = 0; const SAEE_AlreadyRunning = 1; const SAEE_NotRunning = 2; const SAEE_NoTimer = 3; const SAEE_NoMemory = 4; const SAEE_Assert = 5; const SAEE_Internal = 6; const SAEE_Config_Invalid = 10; const SAEE_Config_Compressed = 11; const SAEE_CPU_Internal = 20; const SAEE_CPU_Requires68020 = 21; const SAEE_CPU_Requires680EC20 = 22; const SAEE_CPU_Requires68030 = 23; const SAEE_CPU_Requires68040 = 24; const SAEE_Memory_NoKickstartRom = 30; const SAEE_Memory_NoExtendedRom = 31; const SAEE_Memory_RomSize = 32; const SAEE_Memory_RomKey = 33; const SAEE_Memory_RomDecode = 34; const SAEE_Memory_RomChecksum = 35; const SAEE_Memory_RomUnknown = 36; const SAEE_Video_ElementNotFound = 40; const SAEE_Video_RequiresCanvas = 41; const SAEE_Video_RequiresWegGl = 42; const SAEE_Video_ComphileShader = 43; const SAEE_Video_LinkShader = 44; const SAEE_Audio_RequiresWebAudio = 50; /*-----------------------------------------------------------------------*/ /* global references */ var SAER = null; /*---------------------------------*/ /* global constants */ const SAEC_spcflag_STOP = 2; const SAEC_spcflag_COPPER = 4; const SAEC_spcflag_INT = 8; const SAEC_spcflag_BRK = 16; //const SAEC_spcflag_UAEINT = 32; const SAEC_spcflag_TRACE = 64; const SAEC_spcflag_DOTRACE = 128; const SAEC_spcflag_DOINT = 256; const SAEC_spcflag_BLTNASTY = 512; //const SAEC_spcflag_EXEC = 1024; //const SAEC_spcflag_ACTION_REPLAY = 2048; //const SAEC_spcflag_TRAP = 4096; /* enforcer-hack */ const SAEC_spcflag_MODE_CHANGE = 8192; const SAEC_spcflag_CHECK = 32768; const SAEC_command_Quit = 1; const SAEC_command_Reset = 2; const SAEC_command_KeyboardReset = 3; const SAEC_command_HardReset = 4; const SAEC_command_Pause = 5; const SAEC_command_Resume = 6; /*---------------------------------*/ const SAEC_Info_Brower_ID_Unknown = 0; const SAEC_Info_Brower_ID_Chrome = 1; const SAEC_Info_Brower_ID_Safari = 2; const SAEC_Info_Brower_ID_Opera = 3; const SAEC_Info_Brower_ID_Firefox = 4; const SAEC_Info_Brower_ID_InternetExplorer = 5; const SAEC_info = (function() { var info = { browser: { id: SAEC_Info_Brower_ID_Unknown, name: "Unknown", plat: "Unknown", lang: "en" }, memory: { maxSize: 0 }, audio: { webAudio: false }, video: { canvas: false, webGL: false } }; /* browser */ if (navigator.userAgent.indexOf("Chrome") > -1) { info.browser.id = SAEC_Info_Brower_ID_Chrome; info.browser.name = "Google Chrome"; } else if (navigator.userAgent.indexOf("Safari") > -1) { info.browser.id = SAEC_Info_Brower_ID_Safari; info.browser.name = "Apple Safari"; } else if (navigator.userAgent.indexOf("Opera") > -1) { info.browser.id = SAEC_Info_Brower_ID_Opera; info.browser.name = "Opera"; } else if (navigator.userAgent.indexOf("Firefox") > -1) { info.browser.id = SAEC_Info_Brower_ID_Firefox; info.browser.name = "Mozilla Firefox"; } else if (navigator.userAgent.indexOf("MSIE") > -1) { info.browser.id = SAEC_Info_Brower_ID_InternetExplorer; info.browser.name = "Microsoft Internet Explorer"; } info.browser.plat = navigator.platform; info.browser.lang = navigator.language; /* max memory */ if (0) { var size = 1048576; while (true) { try { var data = new Uint8Array(size); delete data; info.memory.maxSize = size; } catch (e) { break; } size *= 2; } } else info.memory.maxSize = 1073741824; //1G /* audio */ var audioContext = null; try { var audioContextDriver = window.AudioContext || window.webkitAudioContext; audioContext = new audioContextDriver(); var audioProcessor = audioContext.createScriptProcessor(1024, 2, 2); info.audio.webAudio = true; if (audioContext.close) audioContext.close().then(function() {}); audioContext = null; } catch (e) { if (audioContext) { if (audioContext.close) audioContext.close().then(function() {}); audioContext = null; } } /* video */ var canvas = document.createElement("canvas"); if (canvas && canvas.getContext) { try { var ctx = canvas.getContext("2d"); var imageData = ctx.createImageData(16, 16); info.video.canvas = true; try { const glParams = { alpha: false, depth: true, stencil: false, antialias: false, premultipliedAlpha: false, preserveDrawingBuffer: true, failIfMajorPerformanceCaveat: false }; ctx = canvas.getContext("webgl", glParams) || canvas.getContext("experimental-webgl", glParams); info.video.webGL = true; } catch(e) {} } catch(e) {} } return info; })(); /*---------------------------------*/ /* global variables */ var SAEV_spcflags = 0; var SAEV_command = 0; /*---------------------------------*/ /* global functions */ function SAEF_setSpcFlags(x) { SAEV_spcflags |= x; }; function SAEF_clrSpcFlags(x) { SAEV_spcflags &= ~x; }; /*---------------------------------*/ function SAEF_now() { return Math.floor(performance.now() * 1000); /* micro-seconds since page-load */ } function SAEF_sleep(ms) { var start = performance.now(); while ((performance.now() - start) < ms) {} /* pretty nasty */ } /*---------------------------------*/ /* debug */ function SAEF_log() { if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Log && arguments.length) { var str = sprintf.apply(this, arguments); if (console.log) console.log(str); } } function SAEF_info() { if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Info && arguments.length) { var str = sprintf.apply(this, arguments); if (console.info) console.info(str); } } function SAEF_warn() { if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Warn && arguments.length) { var str = sprintf.apply(this, arguments); if (console.warn) console.warn(str); } } function SAEF_error() { if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Error && arguments.length) { var str = sprintf.apply(this, arguments); if (console.error) console.error(str); } } function SAEF_fatal() { var argumentsArray = Array.prototype.slice.call(arguments); var err = argumentsArray[0]; var str = sprintf.apply(this, argumentsArray.slice(1)); if (console.error) console.error(str); throw new SAEO_Error(err, str); } function SAEF_assert(cond) { if (!cond) { var err = SAEE_Assert; var str = "Assertion failed. This is a bug in SAE."; if (console.error) console.error(str); throw new SAEO_Error(err, str); } } /*---------------------------------*/ function ScriptedAmigaEmulator() { SAER = this; this.autoconf = new SAEO_AutoConf(); this.audio = new SAEO_Audio(); this.blitter = new SAEO_Blitter(); this.cia = new SAEO_CIA(); this.config = new SAEO_Configuration(); this.copper = new SAEO_Copper(); this.cpu = new SAEO_CPU(); this.custom = new SAEO_Custom(); this.devices = new SAEO_Devices(); this.disk = new SAEO_Disk(); this.events = new SAEO_Events(); this.expansion = new SAEO_Expansion(); this.filesys = new SAEO_Filesys(); this.gayle = new SAEO_Gayle(); this.gui = new SAEO_GUI(); this.hardfile = new SAEO_Hardfile(); this.ide = new SAEO_IDE(); this.input = new SAEO_Input(); this.m68k = new SAEO_M68K(); this.memory = new SAEO_Memory(); this.playfield = new SAEO_Playfield(); this.roms = new SAEO_Roms(); this.rtc = new SAEO_RTC(); this.serial = new SAEO_Serial(); this.video = new SAEO_Video(); /*---------------------------------*/ this.running = false; this.paused = false; /*-----------------------------------------------------------------------*/ this.dump = function () { this.m68k.dump(); //this.memory.dump(); //this.cia.dump(); }; /*-----------------------------------------------------------------------*/ this.do_start_program = function() { if (SAEV_command >= 0) SAEV_command = SAEC_command_Reset; this.m68k.m68k_go(true); } this.do_leave_program = function() { //sampler_free(); this.video.cleanup(); this.input.cleanup(); this.disk.cleanup(); this.audio.cleanup(); //dump_counts(); this.serial.cleanup(); /*#ifdef CDTV cdtv_free(); cdtvcr_free(); #endif #ifdef CD32 akiko_free(); cd32_fmv_free(); #endif*/ //this.gui.cleanup(); //empty //#ifdef AUTOCONFIG this.expansion.cleanup(); //#endif //#ifdef FILESYS this.filesys.cleanup(); //#endif this.gayle.cleanup(); /*idecontroller_free(); device_func_reset(); #ifdef WITH_TOCCATA sndboard_free(); #endif*/ this.memory.cleanup(); //free_shm(); this.autoconf.cleanup(); } this.start_program = function() { this.do_start_program(); if (typeof SAEV_config.hook.event.started === "function") SAEV_config.hook.event.started(); } this.leave_program = function() { this.dump(); this.do_leave_program(); if (typeof SAEV_config.hook.event.stopped === "function") SAEV_config.hook.event.stopped(); } this.pause_program = function(p) { this.audio.pauseResume(p); this.events.pauseResume(p); if (typeof SAEV_config.hook.event.paused === "function") SAEV_config.hook.event.paused(p); } /*---------------------------------*/ /* API */ this.getVersion = function(str) { if (str) return sprintf("%d.%d.%d", SAEC_Version, SAEC_Revision, SAEC_Patch); else return [SAEC_Version, SAEC_Revision, SAEC_Patch]; } this.getInfo = function() { return SAEC_info; } this.getConfig = function() { return SAEV_config; } this.setDefaults = function() { return this.config.setDefaults(); } this.setModel = function(model, config) { return this.config.setModel(model, config); } this.setMountInfoDefaults = function(num) { var ci = SAEV_config.mount.config[num].ci; this.filesys.uci_set_defaults(ci, false); } this.start = function() { if (SAER.running) { SAEF_warn("sae.start() emulation already running"); return SAEE_AlreadyRunning; } SAEF_info("sae.start() starting..."); var err; if ((err = this.config.setup()) != SAEE_None) return err; if ((err = this.video.obtain()) != SAEE_None) return err; if ((err = this.audio.obtain()) != SAEE_None) return err; this.input.setup(); //inputdevice_init(); if ((err = this.gui.setup()) != SAEE_None) return err; /*#ifdef PICASSO96 picasso_reset(); #endif*/ //this.config.fixup_prefs(currprefs, true); //SAEV_config.audio.mode = 0; /* force sound settings change */ this.memory.hardreset(2); if ((err = this.memory.reset(true)) == SAEE_None) { /*#ifdef AUTOCONFIG native2amiga_install(); #endif*/ this.custom.setup(); //OWN this.blitter.setup(); //OWN this.playfield.setup(); //custom_init(); this.serial.setup(); this.disk.setup(); this.events.reset_frame_rate_hack(); if ((err = this.m68k.setup()) == SAEE_None) /* m68k_init() must come after reset_frame_rate_hack() */ { //this.gui.update(); //empty if ((err = this.video.setup(true)) == SAEE_None) { if ((err = this.audio.setup()) == SAEE_None) { this.start_program(); SAEF_info("sae.start() ...done"); return SAEE_None; } this.video.cleanup(); } } } this.input.cleanup(); SAEF_error("sae.start() ...error %d", err); return err; } this.stop = function() { //uae_quit() if (this.running) { SAEF_info("sae.stop()"); if (SAEV_command != -SAEC_command_Quit) SAEV_command = -SAEC_command_Quit; return SAEE_None; } else { SAEF_warn("sae.stop() emulation not running"); return SAEE_NotRunning; } }; this.reset = function(hard, keyboard) { //uae_reset(hard, keyboard) if (typeof hard == "undefined") var hard = false; if (typeof keyboard == "undefined") var keyboard = false; if (this.running) { SAEF_info("sae.reset() hard %d, keyboard %d", hard?1:0, keyboard?1:0); if (SAEV_command == 0) { SAEV_command = -SAEC_command_Reset; /* soft */ if (keyboard) SAEV_command = -SAEC_command_KeyboardReset; if (hard) SAEV_command = -SAEC_command_HardReset; } return SAEE_None; } else { SAEF_warn("sae.reset() emulation not running"); return SAEE_NotRunning; } }; this.pause = function(pause) { if (this.running) { if (!this.paused && pause) { SAEF_info("sae.pause() pausing emulation"); SAEV_command = SAEC_command_Pause; } else if (this.paused && !pause) { SAEF_info("sae.pause() resuming emulation"); SAEV_command = SAEC_command_Resume; } return SAEE_None; } else { SAEF_warn("sae.pause() emulation not running"); return SAEE_NotRunning; } }; this.insert = function(unit) { if (this.running) { var file = SAEV_config.floppy.drive[unit].file; this.disk.insert(unit, file); SAEF_info("sae.insert() unit %d inserted, name '%s', size %d, protected %d", unit, file.name, file.size, file.prot?1:0); return SAEE_None; } else { SAEF_warn("sae.insert() emulation not running"); return SAEE_NotRunning; } }; this.eject = function(unit) { if (this.running) { this.disk.eject(unit); SAEF_info("sae.eject() unit %d ejected", unit); return SAEE_None; } else { SAEF_warn("sae.eject() emulation not running"); return SAEE_NotRunning; } }; this.getRomInfo = function(ri, file) { return this.roms.examine(ri, file); }; this.getDiskInfo = function(di, unit) { return this.disk.examine(di, unit); }; this.createDisk = function(unit, name, mode, type, label, ffs, bootable) { if (!this.disk.create(unit, name, mode, type, label, ffs, bootable)) return SAEE_NoMemory; return SAEE_None; }; this.mute = function(mute) { if (this.running) { this.audio.mute(mute); if (mute) SAEF_log("sae.mute() audio muted"); else SAEF_log("sae.mute() playing audio"); return SAEE_None; } else { SAEF_warn("sae.mute() emulation not running"); return SAEE_NotRunning; } }; this.keyPress = function(e, down) { this.input.keyPress(e, down); return SAEE_None; }; /*---------------------------------*/ SAEF_info("SAE %d.%d.%d", SAEC_Version, SAEC_Revision, SAEC_Patch); }