├── .gitignore ├── Makefile ├── README.md ├── disass.htm ├── disass.js ├── example.css ├── example.htm ├── example.js ├── example2.htm ├── favicon.ico ├── gpl.htm ├── images └── logo.png ├── index.css ├── index.htm ├── index.js ├── readme.htm └── sae ├── amiga.js ├── audio.js ├── autoconf.js ├── blitter.js ├── cia.js ├── config.js ├── copper.js ├── cpu.js ├── custom.js ├── disassembler.js ├── disk.js ├── dms.js ├── dongle.js ├── events.js ├── expansion.js ├── filesys.js ├── gayle.js ├── hardfile.js ├── ide.js ├── input.js ├── m68k.js ├── memory.js ├── playfield.js ├── prototypes.js ├── roms.js ├── rtc.js ├── serpar.js ├── utils.js └── video.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.adf 2 | db/tools/index.htm 3 | db/index.htm 4 | db/games/index.htm 5 | db/demos_aga/index.htm 6 | db/demos/index.htm 7 | *.bin 8 | scriptedamigaemulator.js 9 | scriptedamigaemulator.js.map 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | scriptedamigaemulator.js: sae/prototypes.js sae/utils.js sae/dms.js sae/config.js sae/roms.js sae/memory.js sae/autoconf.js sae/expansion.js sae/events.js sae/gayle.js sae/ide.js sae/filesys.js sae/hardfile.js sae/input.js sae/serial.js sae/custom.js sae/blitter.js sae/copper.js sae/playfield.js sae/video.js sae/audio.js sae/cia.js sae/disk.js sae/rtc.js sae/m68k.js sae/cpu.js sae/amiga.js 2 | closure-compiler --language_in=ECMASCRIPT6 --language_out ES5 $^ --js_output_file $@ --create_source_map $@.map 3 | printf "/*\n//@ sourceMappingURL=%b\n*/" $@.map >> $@ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Scripted Amiga Emulator 2 | ======================= 3 | 4 | Amiga Emulator in javascript and HTML5. 5 | 6 | Visit https://scriptedamigaemulator.net -------------------------------------------------------------------------------- /disass.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SAE - Scripted Amiga Emulator 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 25 |
26 | Here you can use the internal disassembler. Code-types are from 68000-68030, no FPU, no MMU.
27 | Note: No files or informations about files are transferred to the internet. Disassembling is done in the browser, using javascript. 28 |
29 | 30 | 31 | 32 | 37 | 38 |
File 33 | <unset> (required) 34 | 35 | 36 |
39 |
40 | 41 | 42 | 47 | 48 | 49 | 68 | 69 | 70 | 76 | 77 |
43 | Offset $ 44 | Limit instructions 45 | 46 |
50 | Radix 51 | 55 | Hex-prefix 56 | 60 | Hex-width 61 | 66 | Relocate 67 |
71 | Show 72 | Address 73 | Code/Memory 74 | Upper-case 75 |
78 |
79 | 80 |
81 |
82 | 88 |
89 | 90 | 91 | -------------------------------------------------------------------------------- /disass.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: This file does not contain any emulator-code. 18 | -------------------------------------------------------------------------*/ 19 | 20 | var sda = null; /* SDA instance */ 21 | var cfg = null; /* reference to the config-object */ 22 | 23 | 24 | var filesize = 0; 25 | 26 | var showAddr = true; 27 | var showCode = true; 28 | var upperCase = false; 29 | 30 | var result = []; 31 | 32 | /*---------------------------------*/ 33 | 34 | function isHex(str) { 35 | var str_uc = str.toUpperCase(); 36 | for (var i = 0; i < str_uc.length; i++) { 37 | var chr = str_uc.charCodeAt(i); 38 | if (!((chr >= 48 && chr <= 57) || (chr >= 65 && chr <= 70))) 39 | return false; 40 | } 41 | return true; 42 | } 43 | 44 | /*---------------------------------*/ 45 | 46 | function getSelectValue(id) { 47 | var e = document.getElementById(id); 48 | for (var i = 0; i < e.length; i++) { 49 | if (e[i].selected) return e[i].value; 50 | } 51 | return false; 52 | } 53 | 54 | /*---------------------------------*/ 55 | 56 | function loadFile(e, callback) { 57 | var reader = new FileReader(); 58 | reader.onload = callback; 59 | reader.readAsBinaryString(e); 60 | } 61 | 62 | /*---------------------------------*/ 63 | 64 | function result2text() { 65 | var i, j, text = ""; 66 | for (i = 0; i < result.length; i++) { 67 | var addr = result[i][0]; 68 | var code = result[i][1]; 69 | var words = result[i][2]; 70 | var inst = result[i][3]; 71 | 72 | if (showAddr) 73 | text += sprintf(upperCase ? "$%06X " : "$%06x ", addr); 74 | if (showCode) { 75 | for (j = 0; j < words; j++) text += sprintf(upperCase ? "%04X " : "%04x ", code[j]); 76 | //for (j = words; j < 5; j++) text += "     "; 77 | for (j = words; j < 5; j++) text += " "; 78 | } 79 | text += upperCase ? inst.toUpperCase() : inst; 80 | //text += "
"; 81 | text += "\n"; 82 | 83 | if (addr + words*2 >= filesize) break; 84 | } 85 | return text; 86 | } 87 | 88 | /*---------------------------------*/ 89 | 90 | function init() { 91 | try { 92 | sda = new ScriptedDisAssembler(); 93 | cfg = sda.getConfig(); /* reference to config */ 94 | //console.log(cfg); 95 | } catch(e) { 96 | throw e; 97 | } 98 | } 99 | 100 | function disass() { 101 | try { 102 | result = sda.disassemble(); 103 | //console.log(result); 104 | 105 | document.getElementById("disass_code").value = result2text(); 106 | document.getElementById("disass_cfg").style.display = "table"; 107 | document.getElementById("disass_code").style.display = "inline"; 108 | } catch(e) { 109 | throw e; 110 | } 111 | } 112 | 113 | /*---------------------------------*/ 114 | 115 | function updFile() { 116 | var e = document.getElementById("cfg_file").files[0]; 117 | if (!e) return; 118 | 119 | loadFile(e, function (event) { 120 | cfg.code = event.target.result; 121 | cfg.offset = 0; 122 | 123 | filesize = event.target.result.length; 124 | 125 | var fn = document.getElementById("cfg_filename"); 126 | fn.className = ""; 127 | fn.innerHTML = e.name; 128 | document.getElementById("cfg_offset").value = "0"; 129 | 130 | disass(); 131 | }); 132 | } 133 | 134 | function updOffset() { 135 | var offset = document.getElementById("cfg_offset"); 136 | if (isHex(offset.value)) { 137 | var newoffset = parseInt(offset.value, 16); 138 | 139 | if (newoffset < filesize) { 140 | cfg.offset = newoffset; 141 | disass(); 142 | } else { 143 | alert(sprintf("The value at 'Offset' is behing the file-size. (max $%x)", filesize - 1)); 144 | offset.focus(); 145 | } 146 | } else { 147 | alert("The value at 'Offset' in not a hexadecimal number."); 148 | offset.focus(); 149 | } 150 | } 151 | 152 | function updLimit() { 153 | var limit = document.getElementById("cfg_limit"); 154 | //if (isDec(limit.value)) { 155 | cfg.limit = parseInt(limit.value); 156 | disass(); 157 | /*} else { 158 | alert("The value at "Limit" in not a decimal number."); 159 | limit.focus(); 160 | }*/ 161 | } 162 | 163 | function updNext() { 164 | var addr = result[result.length - 1][0]; 165 | var words = result[result.length - 1][2]; 166 | 167 | cfg.offset = addr + words*2; 168 | document.getElementById("cfg_offset").value = sprintf("%x", cfg.offset); 169 | disass(); 170 | } 171 | 172 | function updRadix() { 173 | cfg.radix = parseInt(getSelectValue("cfg_radix")); 174 | disass(); 175 | } 176 | function updPrefx() { 177 | cfg.prefx = parseInt(getSelectValue("cfg_prefx")) == 1 ? "$" : "0x"; 178 | disass(); 179 | } 180 | function updWidth() { 181 | cfg.width = parseInt(getSelectValue("cfg_width")); 182 | disass(); 183 | } 184 | 185 | function updCase() { 186 | upperCase = document.getElementById("cfg_case").checked; 187 | disass(); 188 | } 189 | 190 | function updReloc() { 191 | cfg.reloc = document.getElementById("cfg_reloc").checked; 192 | disass(); 193 | } 194 | 195 | function updShowAddr() { 196 | showAddr = document.getElementById("cfg_showAddr").checked; 197 | disass(); 198 | } 199 | function updShowCode() { 200 | showCode = document.getElementById("cfg_showCode").checked; 201 | disass(); 202 | } 203 | -------------------------------------------------------------------------------- /example.css: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012-2016 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | -------------------------------------------------------------------------*/ 17 | 18 | a:link { color:#24c; text-decoration:none; } 19 | a:visited { color:#24c; text-decoration:none; } 20 | a:hover { color:#24c; text-decoration:underline; } 21 | 22 | body { 23 | top:0; 24 | left:0; 25 | margin:0; 26 | padding:0; 27 | font-family:Verdana; 28 | font-size:13px; 29 | background-color:#f8f8f8; 30 | -webkit-touch-callout:none; 31 | -webkit-user-select:none; 32 | -khtml-user-select:none; 33 | -moz-user-select:none; 34 | -ms-user-select:none; 35 | user-select:none; 36 | } 37 | 38 | table { 39 | white-space:nowrap; 40 | } 41 | 42 | /*---------------------------------*/ 43 | 44 | .gray { color:gray; } 45 | .green { color:green; } 46 | .orange { color:orange; } 47 | .red { color:red; } 48 | 49 | .alt { text-align:left; vertical-align:top; } 50 | .alm { text-align:left; vertical-align:middle; } 51 | .alb { text-align:left; vertical-align:bottom; } 52 | 53 | .act { text-align:center; vertical-align:top; } 54 | .acm { text-align:center; vertical-align:middle; } 55 | .acb { text-align:center; vertical-align:bottom; } 56 | 57 | .art { text-align:right; vertical-align:top; } 58 | .arm { text-align:right; vertical-align:middle; } 59 | .arb { text-align:right; vertical-align:bottom; } 60 | 61 | 62 | .info { 63 | font-size:11px; 64 | color:dimgray; 65 | } 66 | .warn { 67 | font-size:11px; 68 | color:red; 69 | } 70 | 71 | .noscript { 72 | margin:auto; 73 | padding:4px; 74 | font-size:17px; 75 | font-weight:bold; 76 | text-align:center; 77 | color:red; 78 | } 79 | -------------------------------------------------------------------------------- /example.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SAE - Scripted Amiga Emulator 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 70 | 71 | 72 | 73 | 74 | 135 | 136 | 137 | 138 | 139 | 152 | 153 | 154 | 155 | 156 | 168 | 169 |
Status 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
PWRHD000000
69 |
Config 75 | 76 | 77 | 78 | 91 | 92 | 93 | 94 | 105 | 106 | 107 | 108 | 114 | 115 | 116 | 117 | 123 | 124 | 125 | 126 | 132 | 133 |
Amiga Model 79 | 90 |
Resolution 95 | 100 | 104 |
Kickstart ROM 109 | 110 | <unset> (required) 111 | 112 | 113 |
Floppy (DF0) 118 | 119 | <empty> 120 | 121 | 122 |
Floppy (DF1) 127 | 128 | <empty> 129 | 130 | 131 |
134 |
Controls 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
151 |
Output 157 | 158 |
159 | 160 | 161 | 165 | 166 | 167 |
170 |
171 | 172 | 173 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: This file does not contain any emulator-code. 18 | -------------------------------------------------------------------------*/ 19 | 20 | var sae = null; /* SAE instance */ 21 | var cfg = null; /* Reference to the config-object */ 22 | var inf = null; /* Reference to the info-object */ 23 | 24 | var running = false; /* Is the emualtion currently running? */ 25 | var paused = false; /* Is the emualtion currently paused? */ 26 | var screened = false; /* Is the video-output currently fullscreen? */ 27 | var muted = false; /* Is the audio-output currently muted? */ 28 | 29 | /*-----------------------------------------------------------------------*/ 30 | /* Helpers */ 31 | 32 | function getSelect(id, asString) { 33 | if (typeof asString == "undefined") asString = false; 34 | var e = document.getElementById(id); 35 | for (var i = 0; i < e.length; i++) { 36 | if (e[i].selected) 37 | return asString ? e[i].value : Number(e[i].value); 38 | } 39 | return false; 40 | } 41 | 42 | function setSelect(id, v) { 43 | var e = document.getElementById(id); 44 | var vs = String(v); 45 | for (var i = 0; i < e.length; i++) { 46 | if (e[i].value === vs) { 47 | e[i].selected = true; 48 | return; 49 | } 50 | } 51 | } 52 | 53 | function styleDisplayInline(id, show) { 54 | var e = document.getElementById(id); 55 | e.style.display = show ? "inline" : "none"; 56 | } 57 | 58 | function switchPauseResume(p) { 59 | var e = document.getElementById("controls_pr"); 60 | if (p) { 61 | e.innerHTML = "Resume"; 62 | e.onclick = function() { pause(false); }; 63 | } else { 64 | e.innerHTML = "Pause"; 65 | e.onclick = function() { pause(true); }; 66 | } 67 | } 68 | 69 | function switchScreenWindow(s) { 70 | var e = document.getElementById("controls_sw"); 71 | if (s) { 72 | e.innerHTML = "Window"; 73 | e.onclick = function() { screen(false); }; 74 | } else { 75 | e.innerHTML = "Screen"; 76 | e.onclick = function() { screen(true); }; 77 | } 78 | } 79 | 80 | function switchMutePlay(m) { 81 | var e = document.getElementById("controls_mp"); 82 | if (m) { 83 | e.innerHTML = "Play"; 84 | e.onclick = function() { mute(false); }; 85 | } else { 86 | e.innerHTML = "Mute"; 87 | e.onclick = function() { mute(true); }; 88 | } 89 | } 90 | 91 | function saee2text(err) { 92 | switch (err) { 93 | case SAEE_AlreadyRunning: return "The emulator is already running."; 94 | case SAEE_NotRunning: return "The emulator is not running."; 95 | case SAEE_NoTimer: return "No timing-functions avail. Please upgrade your browser."; 96 | case SAEE_NoMemory: return "Out of memory."; 97 | case SAEE_Assert: return "Assertiation failed."; 98 | case SAEE_Internal: return "Internal emulator error."; 99 | case SAEE_Config_Invalid: return "Invalid configuration."; 100 | case SAEE_Config_Compressed: return "A ZIP file was detected. Compressed files are not yet supported."; 101 | case SAEE_CPU_Internal: return "Internal CPU-error."; 102 | case SAEE_CPU_Requires68020: return "The selected kickstart-rom does require a 68020 and 32bit address-space"; 103 | case SAEE_CPU_Requires680EC20: return "The selected kickstart-rom does require a 68020."; 104 | case SAEE_CPU_Requires68030: return "The selected kickstart-rom does require a 68030."; 105 | case SAEE_CPU_Requires68040: return "The selected kickstart-rom does require a 68040/68060."; 106 | case SAEE_Memory_NoKickstartRom: return "The kickstart-rom is missing."; 107 | case SAEE_Memory_NoExtendedRom: return "An extended-rom is required but missing.\n\nGo to the ROM-page and select a rom from disk..."; 108 | case SAEE_Memory_RomSize: return "The kickstart- or extended-rom does have an invalid size."; 109 | case SAEE_Memory_RomKey: return "A ROM-keyfile is required. (Cloanto)"; 110 | case SAEE_Memory_RomDecode: return "Invalid ROM-keyfile. (Cloanto)"; 111 | case SAEE_Memory_RomChecksum: return "Checksum-error at the kickstart- or extended-rom."; 112 | case SAEE_Memory_RomUnknown: return "Unknown ROM."; 113 | case SAEE_Video_ElementNotFound: return "Video DIV-element not found. Check 'cfg.video.id'"; 114 | case SAEE_Video_RequiresCanvas: return "This browser does not support 'Canvas'. Please upgrade to an actual version."; 115 | case SAEE_Video_RequiresWegGl: return "This browser does not support 'WebGL'. Please upgrade to an actual version."; 116 | case SAEE_Video_ComphileShader: return "Can not compile the required shader-program."; 117 | case SAEE_Video_LinkShader: return "Can not link the required shader-program."; 118 | case SAEE_Video_RequiresFullscreen: return "This browser does not support the 'Fullscreen-API'. Please upgrade to an actual version."; 119 | case SAEE_Audio_RequiresWebAudio: return "This browser does not support 'WebAudio'. Please upgrade to an actual version."; 120 | default: return "("+err+")"; 121 | } 122 | } 123 | 124 | function loadFile(e, callback) { 125 | var reader = new FileReader(); 126 | reader.onload = callback; 127 | reader.readAsBinaryString(e); 128 | } 129 | 130 | /*---------------------------------*/ 131 | /* CRC-32 checksumming */ 132 | 133 | const crc32Table = (function() { 134 | var table = new Uint32Array(256); 135 | var n, c, k; 136 | 137 | for (n = 0; n < 256; n++) { 138 | c = n; 139 | for (k = 0; k < 8; k++) 140 | c = ((c >>> 1) ^ (c & 1 ? 0xedb88320 : 0)) >>> 0; 141 | table[n] = c; 142 | } 143 | return table; 144 | })(); 145 | 146 | function crc32(data) { 147 | var length = data.length; 148 | var offset = 0; 149 | var crc = 0xffffffff; 150 | 151 | while (length-- > 0) 152 | crc = crc32Table[(crc ^ data.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8); 153 | 154 | return (crc ^ 0xffffffff) >>> 0; 155 | } 156 | 157 | if (crc32("The quick brown fox jumps over the lazy dog") != 0x414fa339) 158 | alert("CRC32-hash testing failed. SAE will not work. This is an internal bug!?"); 159 | 160 | /*-----------------------------------------------------------------------*/ 161 | /* Config */ 162 | 163 | function fixConfig() { 164 | /* video */ 165 | if (cfg.video.enabled) { 166 | if (!inf.video.canvas && !inf.video.webGL) 167 | cfg.video.enabled = false; 168 | else if (SAEV_config.video.api == SAEC_Config_Video_API_WebGL && !inf.video.webGL) 169 | SAEV_config.video.api = SAEC_Config_Video_API_Canvas; 170 | } 171 | /* audio */ 172 | if (cfg.audio.mode >= SAEC_Config_Audio_Mode_On) { 173 | if (!inf.audio.webAudio) 174 | cfg.audio.mode = SAEC_Config_Audio_Mode_Off_Emul; 175 | } 176 | } 177 | 178 | function setRomName() { 179 | var e = document.getElementById("cfg_rom_name"); 180 | if (cfg.memory.rom.size) { 181 | e.className = ""; 182 | e.innerHTML = cfg.memory.rom.name; 183 | styleDisplayInline("cfg_rom_remove", 1); 184 | } else { 185 | e.className = "red"; 186 | e.innerHTML = "<unset> (required)"; 187 | styleDisplayInline("cfg_rom_remove", 0); 188 | document.getElementById("cfg_rom_file").value = ""; 189 | } 190 | } 191 | 192 | function setFloppyName(n) { 193 | var e = document.getElementById("cfg_df"+n+"_name"); 194 | if (cfg.floppy.drive[n].file.size) { 195 | e.className = ""; 196 | e.innerHTML = cfg.floppy.drive[n].file.name; 197 | styleDisplayInline("cfg_df"+n+"_eject", 1); 198 | } else { 199 | e.className = "gray"; 200 | e.innerHTML = "<unset>"; 201 | styleDisplayInline("cfg_df"+n+"_eject", 0); 202 | document.getElementById("cfg_df"+n+"_file").value = ""; 203 | } 204 | } 205 | 206 | function setConfig() { 207 | fixConfig(); 208 | setSelect("cfg_ntsc", cfg.chipset.ntsc ? 1 : 0); 209 | setRomName(); 210 | setFloppyName(0); 211 | setFloppyName(1); 212 | switch (cfg.video.hresolution) { 213 | case SAEC_Config_Video_HResolution_LoRes: 214 | setSelect("cfg_res", 1); 215 | break; 216 | case SAEC_Config_Video_HResolution_HiRes: 217 | setSelect("cfg_res", 2); 218 | break; 219 | case SAEC_Config_Video_HResolution_SuperHiRes: 220 | setSelect("cfg_res", 3); 221 | break; 222 | } 223 | } 224 | 225 | /*---------------------------------*/ 226 | 227 | function getConfig() { 228 | var model = SAEC_Model_A2000; /* Default model after page-load*/ 229 | var modelSubConfig = 0; /* See config.js */ 230 | 231 | switch (getSelect("cfg_model", true)) { 232 | case "A500": model = SAEC_Model_A500; break; 233 | case "A500P": model = SAEC_Model_A500P; break; 234 | case "A600": model = SAEC_Model_A600; break; 235 | case "A1000": model = SAEC_Model_A1000; break; 236 | case "A1200": model = SAEC_Model_A1200; break; 237 | case "A2000": model = SAEC_Model_A2000; break; 238 | case "A3000": model = SAEC_Model_A3000; break; 239 | case "A4000": model = SAEC_Model_A4000; break; 240 | case "A4000T": model = SAEC_Model_A4000T; break; 241 | /* future. do not use. cd-emulation is not implemented yet. 242 | case "CDTV": model = SAEC_Model_CDTV; break; 243 | case "CD32": model = SAEC_Model_CD32; break; */ 244 | } 245 | /* Set the defaults for the selected model. 246 | ROMs and Floppies are not affected. */ 247 | sae.setModel(model, modelSubConfig); 248 | 249 | /* After here, you may tweak additional settings */ 250 | 251 | cfg.chipset.ntsc = getSelect("cfg_ntsc") == 1; 252 | 253 | cfg.memory.z2FastSize = 2 << 20; /* Give 2mb zorro2 fast-ram */ 254 | 255 | /* Do we have rom-data? */ 256 | if (cfg.memory.rom.size == 0) { 257 | alert(saee2text(SAEE_Memory_NoKickstartRom)); 258 | return false; 259 | } 260 | 261 | cfg.floppy.speed = SAEC_Config_Floppy_Speed_Turbo; /* Set speed to turbo. This is not always compatible */ 262 | 263 | cfg.video.id = "myVideo"; /* Set the id-name of the desired output-div or output-canvas */ 264 | 265 | switch (getSelect("cfg_res")) { 266 | case 1: /* Lores */ 267 | cfg.video.hresolution = SAEC_Config_Video_HResolution_LoRes; 268 | cfg.video.vresolution = SAEC_Config_Video_VResolution_NonDouble; 269 | cfg.video.size_win.width = SAEC_Video_DEF_AMIGA_WIDTH; /* 360 */ 270 | cfg.video.size_win.height = SAEC_Video_DEF_AMIGA_HEIGHT; /* 284 */ 271 | break; 272 | case 2: /* Hires */ 273 | cfg.video.hresolution = SAEC_Config_Video_HResolution_HiRes; 274 | cfg.video.vresolution = SAEC_Config_Video_VResolution_Double; 275 | cfg.video.size_win.width = SAEC_Video_DEF_AMIGA_WIDTH << 1; /* 720 */ 276 | cfg.video.size_win.height = SAEC_Video_DEF_AMIGA_HEIGHT << 1; /* 568 */ 277 | break; 278 | case 3: /* SuperHires */ 279 | cfg.video.hresolution = SAEC_Config_Video_HResolution_SuperHiRes; 280 | cfg.video.vresolution = SAEC_Config_Video_VResolution_Double; 281 | cfg.video.size_win.width = SAEC_Video_DEF_AMIGA_WIDTH << 2; /* 1440 */ 282 | cfg.video.size_win.height = SAEC_Video_DEF_AMIGA_HEIGHT << 1; /* 568 */ 283 | break; 284 | } 285 | 286 | /* Set hooks */ 287 | cfg.hook.log.error = hook_log_error; 288 | 289 | cfg.hook.led.power = hook_led_power; 290 | cfg.hook.led.hd = hook_led_hd; 291 | cfg.hook.led.df = hook_led_df; 292 | cfg.hook.led.fps = hook_led_fps; 293 | cfg.hook.led.cpu = hook_led_cpu; 294 | 295 | cfg.hook.event.started = hook_event_started; 296 | cfg.hook.event.stopped = hook_event_stopped; 297 | cfg.hook.event.reseted = hook_event_reseted; 298 | cfg.hook.event.paused = hook_event_paused; 299 | cfg.hook.event.screened = hook_event_screened; 300 | 301 | /* Enable serial */ 302 | cfg.serial.enabled = true; 303 | cfg.hook.serial.get = hook_serial_get; 304 | cfg.hook.serial.put = hook_serial_put; 305 | 306 | /* Enable parallel */ 307 | cfg.parallel.enabled = true; 308 | cfg.hook.parallel.get = hook_parallel_get; 309 | cfg.hook.parallel.put = hook_parallel_put; 310 | 311 | /* Enable debug-log to developer-console */ 312 | cfg.debug.level = SAEC_Config_Debug_Level_Log; 313 | 314 | /* NOTE: 315 | DO NOT ALTER the config-object while the emulator is running. 316 | 317 | I will release a detailed description of the config-object soon. 318 | */ 319 | return true; 320 | } 321 | 322 | /*---------------------------------*/ 323 | /* Control buttons */ 324 | 325 | function start() { 326 | if (getConfig()) { 327 | /* Start the emulator. This does take some time. 328 | Once started, the hook SAEV_config.hook.event.started will be called. (new 0.9.1) */ 329 | var err = sae.start(); 330 | if (err == SAEE_None) { 331 | /* ... */ 332 | } else 333 | alert(saee2text(err)); 334 | } 335 | } 336 | 337 | function stop() { 338 | /* Send stop-request. This does take some time. 339 | Once stopped, the hook SAEV_config.hook.event.stopped will be called. (new 0.9.1) */ 340 | var err = sae.stop(); 341 | if (err == SAEE_None) { 342 | /* ... */ 343 | } else 344 | alert(saee2text(err)); 345 | } 346 | 347 | function reset() { 348 | /* Send soft-reset request. This does take some time. 349 | Once done, the hook SAEV_config.hook.event.reseted will be called. (new 0.9.1) */ 350 | var hard = false, keyboard = false; 351 | var err = sae.reset(hard, keyboard); 352 | if (err == SAEE_None) { 353 | /* ... */ 354 | } else 355 | alert(saee2text(err)); 356 | } 357 | 358 | function pause(p) { 359 | /* Send Pause- or Resume-request. This does take some time. 360 | Once paused or resumed, the hook SAEV_config.hook.event.paused will be called. (new 0.9.1) 361 | true == pause, false == resume */ 362 | var err = sae.pause(p); 363 | if (err == SAEE_None) { 364 | /* ... */ 365 | } else 366 | alert(saee2text(err)); 367 | } 368 | 369 | function screen(s) { 370 | /* Fullscreen or window. 371 | true == fullscreen, false == window */ 372 | var err = sae.screen(s); 373 | if (err == SAEE_None) { 374 | screened = s; 375 | switchScreenWindow(screened); 376 | /* ... */ 377 | } else 378 | alert(saee2text(err)); 379 | } 380 | 381 | function mute(m) { 382 | /* Mute or play audio. 383 | true == mute, false == play */ 384 | var err = sae.mute(m); 385 | if (err == SAEE_None) { 386 | muted = m; 387 | switchMutePlay(muted); 388 | /* ... */ 389 | } else 390 | alert(saee2text(err)); 391 | } 392 | 393 | function romSelect() { 394 | var e = document.getElementById("cfg_rom_file").files[0]; 395 | if (e) { 396 | loadFile(e, function (event) { 397 | //cfg.memory.rom.path = e.path; currently unused in SAE 398 | cfg.memory.rom.name = e.name; /* filename */ 399 | cfg.memory.rom.data = event.target.result; /* typeof 'String' or 'Uint8Array' */ 400 | cfg.memory.rom.size = e.size; /* size in bytes */ 401 | cfg.memory.rom.crc32 = crc32(event.target.result); /* pre-calculate crc32 for a faster start */ 402 | setRomName(); 403 | 404 | /*ri = new SAEO_RomInfo(); 405 | var err = sae.getRomInfo(ri, cfg.memory.rom); 406 | if (err == SAEE_None) { 407 | examine 'ri'... 408 | }*/ 409 | }); 410 | } 411 | } 412 | function romRemove() { 413 | cfg.memory.rom.clr(); 414 | setRomName(); 415 | } 416 | 417 | function floppyInsert(n) { 418 | var e = document.getElementById("cfg_df"+n+"_file").files[0]; 419 | if (e) { 420 | loadFile(e, function(event) { 421 | var file = cfg.floppy.drive[n].file; 422 | //file.path = e.path; currently unused in SAE 423 | file.name = e.name; /* filename */ 424 | file.data = event.target.result; /* typeof 'String' or 'Uint8Array' */ 425 | file.size = e.size; /* size in bytes */ 426 | file.crc32 = crc32(event.target.result); /* pre-calculate crc32 for a faster start */ 427 | setFloppyName(n); 428 | 429 | /*var di = new SAEO_DiskInfo(); 430 | var err = sae.getDiskInfo(di, n); 431 | if (err == SAEE_None) { 432 | examine 'di'... 433 | }*/ 434 | 435 | /* If the emulator is running, notify about the disk-change */ 436 | if (running) 437 | sae.insert(n); 438 | }); 439 | } 440 | } 441 | function floppyEject(n) { 442 | cfg.floppy.drive[n].file.clr(); 443 | setFloppyName(n); 444 | 445 | /* If the emulator is running, notify about the disk-change */ 446 | if (running) 447 | sae.eject(n); 448 | } 449 | 450 | /*---------------------------------*/ 451 | /* Init-routine, called once on page-load */ 452 | 453 | function init() { 454 | /* Create the emulator */ 455 | sae = new ScriptedAmigaEmulator(); 456 | 457 | /* Get the reference to the info-object. 458 | As alternative, you can use the constant 'SAEC_info' directly, 459 | defined in amiga.js */ 460 | inf = sae.getInfo(); /* or */ 461 | //inf = SAEC_info; 462 | //console.dir(inf); 463 | 464 | /* Get the reference to the config-object. 465 | As alternative, you can use the variable 'SAEV_config' directly, 466 | defined in config.js */ 467 | cfg = sae.getConfig(); /* or */ 468 | //cfg = SAEV_config; 469 | //console.dir(cfg); 470 | 471 | initLEDs(); 472 | setConfig(); 473 | } 474 | 475 | /*-----------------------------------------------------------------------*/ 476 | /* Hooks (callbacks) */ 477 | 478 | /*---------------------------------*/ 479 | /* Logging */ 480 | 481 | /* There is currently only one log-hook, if a fatal error 482 | does occure while the emulator is running. */ 483 | 484 | function hook_log_error(err, msg) { 485 | /* err is a number type. See SAEE_* in amiga.js for the error codes. */ 486 | running = false; 487 | if (msg.length) 488 | alert(msg); 489 | } 490 | 491 | /*---------------------------------*/ 492 | /* Events (new 0.9.1) */ 493 | 494 | /* Get called after the emulator has finished the starting-process. */ 495 | function hook_event_started() { 496 | running = true; 497 | } 498 | 499 | /* Get called after the emulator has finished the stopping-process. */ 500 | function hook_event_stopped() { 501 | running = false; 502 | 503 | if (paused) { 504 | paused = false; 505 | switchPauseResume(paused); 506 | } 507 | if (muted) { 508 | muted = false; 509 | switchMutePlay(muted); 510 | } 511 | if (screened) { 512 | screened = false; 513 | switchScreenWindow(screened); 514 | } 515 | resetLEDs(); 516 | } 517 | 518 | /* Get called after the emulator has finished the reset-routine. */ 519 | function hook_event_reseted(hard) { 520 | if (paused) { 521 | paused = false; 522 | switchPauseResume(paused); 523 | } 524 | if (muted) { 525 | muted = false; 526 | switchMutePlay(muted); 527 | } 528 | /*if (screened) { 529 | screened = false; 530 | switchScreenWindow(screened); 531 | }*/ 532 | } 533 | 534 | /* Get called after the emulator has finished switching between pause-resume. */ 535 | function hook_event_paused(p) { 536 | paused = p; 537 | switchPauseResume(p); 538 | } 539 | 540 | /* Get called after the emulator has finished switching between fullscreen-window. */ 541 | function hook_event_screened(s) { 542 | screened = s; 543 | switchScreenWindow(s); 544 | } 545 | 546 | /*---------------------------------*/ 547 | /* Serial */ 548 | 549 | var ser_in = null; /* buffer for serial-data to be transmited */ 550 | var in_pos = 0; 551 | 552 | /* Put some random-data in the serial-buffer */ 553 | function test_serial_send(bytes) { 554 | if (!bytes || bytes > 1024) bytes = 4; 555 | ser_in = new Uint8Array(bytes); 556 | for (var i = 0; i < bytes; i++) 557 | ser_in[i] = 65 + ((Math.random() * 25) >>> 0); /* ASCII 'A'-'Z' */ 558 | } 559 | 560 | /* Get ALWAYS called if the serial.device is enabled. */ 561 | function hook_serial_get() { 562 | if (ser_in !== null) { 563 | if (in_pos < ser_in.length) 564 | return ser_in[in_pos++]; 565 | else { 566 | ser_in = null; /* terminate transmission */ 567 | in_pos = 0; 568 | } 569 | } 570 | return -1; /* return -1 as default */ 571 | } 572 | 573 | var ser_out = ""; /* buffer for received serial-data */ 574 | 575 | /* Get called if the serial.device want to write a byte. */ 576 | function hook_serial_put(charCode) { 577 | if (charCode >= 32 && charCode < 127) /* printable? */ 578 | ser_out += String.fromCharCode(charCode); 579 | else 580 | ser_out += '.'; 581 | 582 | if (ser_out.length == 76 || charCode == 10) { /* line-full or line-feed? */ 583 | console.log("=== SERIAL DATA START ==="); 584 | console.log(ser_out); 585 | console.log("=== SERIAL DATA END ==="); 586 | ser_out = ""; 587 | } 588 | } 589 | 590 | /*---------------------------------*/ 591 | /* Parallel */ 592 | 593 | /* Get called if the parallel.device want to read a byte. */ 594 | function hook_parallel_get() { 595 | /* Must always return a value between 0-255 */ 596 | return 97 + ((Math.random() * 25) >>> 0); /* ASCII 'a'-'z' */ 597 | } 598 | 599 | var par_out = ""; /* buffer for received parallel-data */ 600 | 601 | /* Get called if the parallel.device want to write a byte. */ 602 | function hook_parallel_put(charCode) { 603 | if (charCode >= 32 && charCode < 127) /* printable? */ 604 | par_out += String.fromCharCode(charCode); 605 | else 606 | par_out += '.'; 607 | 608 | if (par_out.length == 76 || charCode == 10) { /* line-full or line-feed? */ 609 | console.log("=== PARALLEL DATA START ==="); 610 | console.log(par_out); 611 | console.log("=== PARALLEL DATA END ==="); 612 | par_out = ""; 613 | } 614 | } 615 | 616 | /*---------------------------------*/ 617 | /* LEDs */ 618 | 619 | const COL_GRAY = "#000"; 620 | const COL_GREEN = "#8C8"; 621 | const COL_RED = "#E88"; 622 | const COL_ORANGE = "#CC8"; 623 | 624 | /* cache elements */ 625 | var e_led_power = null; 626 | var e_led_hd = null; 627 | var e_led_df = [null,null,null,null]; 628 | var e_led_fps = null; 629 | var e_led_cpu = null; 630 | 631 | /* Power LED: boolean On/Off */ 632 | function hook_led_power(on) { 633 | e_led_power.style.color = on ? COL_GREEN : COL_GRAY; 634 | } 635 | 636 | /* Harddisk-LED */ 637 | function hook_led_hd(rw) { 638 | /* rw 0=Off, 1=Read, 2=Write */ 639 | e_led_hd.style.color = rw == 1 ? COL_GREEN : (rw == 2 ? COL_RED : COL_GRAY); 640 | } 641 | 642 | /* Floppy-LED */ 643 | function hook_led_df(unit, disabled, cylinder, side, rw) { 644 | /* unit 0-3 */ 645 | /* cylinder 0-81 */ 646 | /* side 0-1 */ 647 | /* rw 0=Off, 1=Read, 2=Write */ 648 | if (disabled) { 649 | e_led_df[unit].innerHTML = "-"; 650 | e_led_df[unit].style.color = COL_GRAY; 651 | } else { 652 | e_led_df[unit].innerHTML = String(cylinder); 653 | e_led_df[unit].style.color = rw == 1 ? COL_GREEN : (rw == 2 ? COL_RED : COL_GRAY); 654 | } 655 | } 656 | 657 | /* FPS (Frames Per Second) */ 658 | function hook_led_fps(fps, paused) { 659 | /* fps is a float-number, 0-50, in frames */ 660 | e_led_fps.innerHTML = paused ? "0.0" : fps.toFixed(1); 661 | e_led_fps.style.color = COL_GRAY; 662 | } 663 | 664 | /* CPU-usage */ 665 | function hook_led_cpu(usage, paused) { 666 | /* usage is a float-number, 0-1000, in percent */ 667 | if (paused) { 668 | e_led_cpu.innerHTML = "0%"; 669 | e_led_cpu.style.color = COL_GRAY; 670 | } else { 671 | e_led_cpu.innerHTML = usage.toFixed(0) + "%"; 672 | if (usage < 90) 673 | e_led_cpu.style.color = COL_GREEN; 674 | else if (usage < 110) 675 | e_led_cpu.style.color = COL_ORANGE; 676 | else 677 | e_led_cpu.style.color = COL_RED; 678 | } 679 | } 680 | 681 | function resetLEDs() { 682 | hook_led_power(false); 683 | hook_led_hd(0); 684 | hook_led_df(0, true, 0, 0, 0); 685 | hook_led_df(1, true, 0, 0, 0); 686 | hook_led_df(2, true, 0, 0, 0); 687 | hook_led_df(3, true, 0, 0, 0); 688 | hook_led_fps(0, false); 689 | hook_led_cpu(0, false); 690 | } 691 | 692 | function initLEDs() { 693 | e_led_power = document.getElementById("status_led_power"); 694 | e_led_hd = document.getElementById("status_led_hd"); 695 | e_led_df[0] = document.getElementById("status_led_df0"); 696 | e_led_df[1] = document.getElementById("status_led_df1"); 697 | e_led_df[2] = document.getElementById("status_led_df2"); 698 | e_led_df[3] = document.getElementById("status_led_df3"); 699 | e_led_fps = document.getElementById("status_led_fps"); 700 | e_led_cpu = document.getElementById("status_led_cpu"); 701 | 702 | resetLEDs(); 703 | } 704 | -------------------------------------------------------------------------------- /example2.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SAE - Scripted Amiga Emulator 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 |
50 |
51 | 52 | 53 | 54 | 55 | 56 | 70 | 71 | 72 | 73 | 74 | 135 | 136 | 137 | 138 | 139 | 151 | 152 | 153 | 154 | 155 | 167 | 168 |
Status 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
PWRHD000000
69 |
Config 75 | 76 | 77 | 78 | 91 | 92 | 93 | 94 | 105 | 106 | 107 | 108 | 114 | 115 | 116 | 117 | 123 | 124 | 125 | 126 | 132 | 133 |
Amiga Model 79 | 90 |
Resolution 95 | 100 | 104 |
Kickstart ROM 109 | 110 | <unset> (required) 111 | 112 | 113 |
Floppy (DF0) 118 | 119 | <empty> 120 | 121 | 122 |
Floppy (DF1) 127 | 128 | <empty> 129 | 130 | 131 |
134 |
Controls 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 |
150 |
Output 156 | 159 | 160 | 161 | 162 | 163 | 165 | 166 |
169 |
170 | 171 | 172 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naTmeg/ScriptedAmigaEmulator/7fc423194d9690dc0f4344afe638aaf580e5e8a9/favicon.ico -------------------------------------------------------------------------------- /gpl.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF) 6 | 8 | 9 | 10 |

GNU GENERAL PUBLIC LICENSE

11 |

12 | Version 2, June 1991 13 |

14 | 15 |
 16 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
 17 | 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 18 | 
 19 | Everyone is permitted to copy and distribute verbatim copies
 20 | of this license document, but changing it is not allowed.
 21 | 
22 | 23 |

Preamble

24 | 25 |

26 | The licenses for most software are designed to take away your 27 | freedom to share and change it. By contrast, the GNU General Public 28 | License is intended to guarantee your freedom to share and change free 29 | software--to make sure the software is free for all its users. This 30 | General Public License applies to most of the Free Software 31 | Foundation's software and to any other program whose authors commit to 32 | using it. (Some other Free Software Foundation software is covered by 33 | the GNU Lesser General Public License instead.) You can apply it to 34 | your programs, too. 35 |

36 | 37 |

38 | When we speak of free software, we are referring to freedom, not 39 | price. Our General Public Licenses are designed to make sure that you 40 | have the freedom to distribute copies of free software (and charge for 41 | this service if you wish), that you receive source code or can get it 42 | if you want it, that you can change the software or use pieces of it 43 | in new free programs; and that you know you can do these things. 44 |

45 | 46 |

47 | To protect your rights, we need to make restrictions that forbid 48 | anyone to deny you these rights or to ask you to surrender the rights. 49 | These restrictions translate to certain responsibilities for you if you 50 | distribute copies of the software, or if you modify it. 51 |

52 | 53 |

54 | For example, if you distribute copies of such a program, whether 55 | gratis or for a fee, you must give the recipients all the rights that 56 | you have. You must make sure that they, too, receive or can get the 57 | source code. And you must show them these terms so they know their 58 | rights. 59 |

60 | 61 |

62 | We protect your rights with two steps: (1) copyright the software, and 63 | (2) offer you this license which gives you legal permission to copy, 64 | distribute and/or modify the software. 65 |

66 | 67 |

68 | Also, for each author's protection and ours, we want to make certain 69 | that everyone understands that there is no warranty for this free 70 | software. If the software is modified by someone else and passed on, we 71 | want its recipients to know that what they have is not the original, so 72 | that any problems introduced by others will not reflect on the original 73 | authors' reputations. 74 |

75 | 76 |

77 | Finally, any free program is threatened constantly by software 78 | patents. We wish to avoid the danger that redistributors of a free 79 | program will individually obtain patent licenses, in effect making the 80 | program proprietary. To prevent this, we have made it clear that any 81 | patent must be licensed for everyone's free use or not licensed at all. 82 |

83 | 84 |

85 | The precise terms and conditions for copying, distribution and 86 | modification follow. 87 |

88 | 89 | 90 |

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

91 | 92 | 93 |

94 | 0. 95 | This License applies to any program or other work which contains 96 | a notice placed by the copyright holder saying it may be distributed 97 | under the terms of this General Public License. The "Program", below, 98 | refers to any such program or work, and a "work based on the Program" 99 | means either the Program or any derivative work under copyright law: 100 | that is to say, a work containing the Program or a portion of it, 101 | either verbatim or with modifications and/or translated into another 102 | language. (Hereinafter, translation is included without limitation in 103 | the term "modification".) Each licensee is addressed as "you". 104 |

105 | 106 |

107 | Activities other than copying, distribution and modification are not 108 | covered by this License; they are outside its scope. The act of 109 | running the Program is not restricted, and the output from the Program 110 | is covered only if its contents constitute a work based on the 111 | Program (independent of having been made by running the Program). 112 | Whether that is true depends on what the Program does. 113 |

114 | 115 |

116 | 1. 117 | You may copy and distribute verbatim copies of the Program's 118 | source code as you receive it, in any medium, provided that you 119 | conspicuously and appropriately publish on each copy an appropriate 120 | copyright notice and disclaimer of warranty; keep intact all the 121 | notices that refer to this License and to the absence of any warranty; 122 | and give any other recipients of the Program a copy of this License 123 | along with the Program. 124 |

125 | 126 |

127 | You may charge a fee for the physical act of transferring a copy, and 128 | you may at your option offer warranty protection in exchange for a fee. 129 |

130 | 131 |

132 | 2. 133 | You may modify your copy or copies of the Program or any portion 134 | of it, thus forming a work based on the Program, and copy and 135 | distribute such modifications or work under the terms of Section 1 136 | above, provided that you also meet all of these conditions: 137 |

138 | 139 |
140 |
141 |
142 | a) 143 | You must cause the modified files to carry prominent notices 144 | stating that you changed the files and the date of any change. 145 |
146 |
147 |
148 | b) 149 | You must cause any work that you distribute or publish, that in 150 | whole or in part contains or is derived from the Program or any 151 | part thereof, to be licensed as a whole at no charge to all third 152 | parties under the terms of this License. 153 |
154 |
155 |
156 | c) 157 | If the modified program normally reads commands interactively 158 | when run, you must cause it, when started running for such 159 | interactive use in the most ordinary way, to print or display an 160 | announcement including an appropriate copyright notice and a 161 | notice that there is no warranty (or else, saying that you provide 162 | a warranty) and that users may redistribute the program under 163 | these conditions, and telling the user how to view a copy of this 164 | License. (Exception: if the Program itself is interactive but 165 | does not normally print such an announcement, your work based on 166 | the Program is not required to print an announcement.) 167 |
168 |
169 | 170 |

171 | These requirements apply to the modified work as a whole. If 172 | identifiable sections of that work are not derived from the Program, 173 | and can be reasonably considered independent and separate works in 174 | themselves, then this License, and its terms, do not apply to those 175 | sections when you distribute them as separate works. But when you 176 | distribute the same sections as part of a whole which is a work based 177 | on the Program, the distribution of the whole must be on the terms of 178 | this License, whose permissions for other licensees extend to the 179 | entire whole, and thus to each and every part regardless of who wrote it. 180 |

181 | 182 |

183 | Thus, it is not the intent of this section to claim rights or contest 184 | your rights to work written entirely by you; rather, the intent is to 185 | exercise the right to control the distribution of derivative or 186 | collective works based on the Program. 187 |

188 | 189 |

190 | In addition, mere aggregation of another work not based on the Program 191 | with the Program (or with a work based on the Program) on a volume of 192 | a storage or distribution medium does not bring the other work under 193 | the scope of this License. 194 |

195 | 196 |

197 | 3. 198 | You may copy and distribute the Program (or a work based on it, 199 | under Section 2) in object code or executable form under the terms of 200 | Sections 1 and 2 above provided that you also do one of the following: 201 |

202 | 203 | 204 | 205 | 206 |
207 |
208 |
209 | a) 210 | Accompany it with the complete corresponding machine-readable 211 | source code, which must be distributed under the terms of Sections 212 | 1 and 2 above on a medium customarily used for software interchange; or, 213 |
214 |
215 |
216 | b) 217 | Accompany it with a written offer, valid for at least three 218 | years, to give any third party, for a charge no more than your 219 | cost of physically performing source distribution, a complete 220 | machine-readable copy of the corresponding source code, to be 221 | distributed under the terms of Sections 1 and 2 above on a medium 222 | customarily used for software interchange; or, 223 |
224 |
225 |
226 | c) 227 | Accompany it with the information you received as to the offer 228 | to distribute corresponding source code. (This alternative is 229 | allowed only for noncommercial distribution and only if you 230 | received the program in object code or executable form with such 231 | an offer, in accord with Subsection b above.) 232 |
233 |
234 | 235 |

236 | The source code for a work means the preferred form of the work for 237 | making modifications to it. For an executable work, complete source 238 | code means all the source code for all modules it contains, plus any 239 | associated interface definition files, plus the scripts used to 240 | control compilation and installation of the executable. However, as a 241 | special exception, the source code distributed need not include 242 | anything that is normally distributed (in either source or binary 243 | form) with the major components (compiler, kernel, and so on) of the 244 | operating system on which the executable runs, unless that component 245 | itself accompanies the executable. 246 |

247 | 248 |

249 | If distribution of executable or object code is made by offering 250 | access to copy from a designated place, then offering equivalent 251 | access to copy the source code from the same place counts as 252 | distribution of the source code, even though third parties are not 253 | compelled to copy the source along with the object code. 254 |

255 | 256 |

257 | 4. 258 | You may not copy, modify, sublicense, or distribute the Program 259 | except as expressly provided under this License. Any attempt 260 | otherwise to copy, modify, sublicense or distribute the Program is 261 | void, and will automatically terminate your rights under this License. 262 | However, parties who have received copies, or rights, from you under 263 | this License will not have their licenses terminated so long as such 264 | parties remain in full compliance. 265 |

266 | 267 |

268 | 5. 269 | You are not required to accept this License, since you have not 270 | signed it. However, nothing else grants you permission to modify or 271 | distribute the Program or its derivative works. These actions are 272 | prohibited by law if you do not accept this License. Therefore, by 273 | modifying or distributing the Program (or any work based on the 274 | Program), you indicate your acceptance of this License to do so, and 275 | all its terms and conditions for copying, distributing or modifying 276 | the Program or works based on it. 277 |

278 | 279 |

280 | 6. 281 | Each time you redistribute the Program (or any work based on the 282 | Program), the recipient automatically receives a license from the 283 | original licensor to copy, distribute or modify the Program subject to 284 | these terms and conditions. You may not impose any further 285 | restrictions on the recipients' exercise of the rights granted herein. 286 | You are not responsible for enforcing compliance by third parties to 287 | this License. 288 |

289 | 290 |

291 | 7. 292 | If, as a consequence of a court judgment or allegation of patent 293 | infringement or for any other reason (not limited to patent issues), 294 | conditions are imposed on you (whether by court order, agreement or 295 | otherwise) that contradict the conditions of this License, they do not 296 | excuse you from the conditions of this License. If you cannot 297 | distribute so as to satisfy simultaneously your obligations under this 298 | License and any other pertinent obligations, then as a consequence you 299 | may not distribute the Program at all. For example, if a patent 300 | license would not permit royalty-free redistribution of the Program by 301 | all those who receive copies directly or indirectly through you, then 302 | the only way you could satisfy both it and this License would be to 303 | refrain entirely from distribution of the Program. 304 |

305 | 306 |

307 | If any portion of this section is held invalid or unenforceable under 308 | any particular circumstance, the balance of the section is intended to 309 | apply and the section as a whole is intended to apply in other 310 | circumstances. 311 |

312 | 313 |

314 | It is not the purpose of this section to induce you to infringe any 315 | patents or other property right claims or to contest validity of any 316 | such claims; this section has the sole purpose of protecting the 317 | integrity of the free software distribution system, which is 318 | implemented by public license practices. Many people have made 319 | generous contributions to the wide range of software distributed 320 | through that system in reliance on consistent application of that 321 | system; it is up to the author/donor to decide if he or she is willing 322 | to distribute software through any other system and a licensee cannot 323 | impose that choice. 324 |

325 | 326 |

327 | This section is intended to make thoroughly clear what is believed to 328 | be a consequence of the rest of this License. 329 |

330 | 331 |

332 | 8. 333 | If the distribution and/or use of the Program is restricted in 334 | certain countries either by patents or by copyrighted interfaces, the 335 | original copyright holder who places the Program under this License 336 | may add an explicit geographical distribution limitation excluding 337 | those countries, so that distribution is permitted only in or among 338 | countries not thus excluded. In such case, this License incorporates 339 | the limitation as if written in the body of this License. 340 |

341 | 342 |

343 | 9. 344 | The Free Software Foundation may publish revised and/or new versions 345 | of the General Public License from time to time. Such new versions will 346 | be similar in spirit to the present version, but may differ in detail to 347 | address new problems or concerns. 348 |

349 | 350 |

351 | Each version is given a distinguishing version number. If the Program 352 | specifies a version number of this License which applies to it and "any 353 | later version", you have the option of following the terms and conditions 354 | either of that version or of any later version published by the Free 355 | Software Foundation. If the Program does not specify a version number of 356 | this License, you may choose any version ever published by the Free Software 357 | Foundation. 358 |

359 | 360 |

361 | 10. 362 | If you wish to incorporate parts of the Program into other free 363 | programs whose distribution conditions are different, write to the author 364 | to ask for permission. For software which is copyrighted by the Free 365 | Software Foundation, write to the Free Software Foundation; we sometimes 366 | make exceptions for this. Our decision will be guided by the two goals 367 | of preserving the free status of all derivatives of our free software and 368 | of promoting the sharing and reuse of software generally. 369 |

370 | 371 |

NO WARRANTY

372 | 373 |

374 | 11. 375 | BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 376 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 377 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 378 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 379 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 380 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 381 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 382 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 383 | REPAIR OR CORRECTION. 384 |

385 | 386 |

387 | 12. 388 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 389 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 390 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 391 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 392 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 393 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 394 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 395 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 396 | POSSIBILITY OF SUCH DAMAGES. 397 |

398 | 399 |

END OF TERMS AND CONDITIONS

400 | 401 |

How to Apply These Terms to Your New Programs

402 | 403 |

404 | If you develop a new program, and you want it to be of the greatest 405 | possible use to the public, the best way to achieve this is to make it 406 | free software which everyone can redistribute and change under these terms. 407 |

408 | 409 |

410 | To do so, attach the following notices to the program. It is safest 411 | to attach them to the start of each source file to most effectively 412 | convey the exclusion of warranty; and each file should have at least 413 | the "copyright" line and a pointer to where the full notice is found. 414 |

415 | 416 |
417 | one line to give the program's name and an idea of what it does.
418 | Copyright (C) yyyy  name of author
419 | 
420 | This program is free software; you can redistribute it and/or
421 | modify it under the terms of the GNU General Public License
422 | as published by the Free Software Foundation; either version 2
423 | of the License, or (at your option) any later version.
424 | 
425 | This program is distributed in the hope that it will be useful,
426 | but WITHOUT ANY WARRANTY; without even the implied warranty of
427 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
428 | GNU General Public License for more details.
429 | 
430 | You should have received a copy of the GNU General Public License
431 | along with this program; if not, write to the Free Software
432 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
433 | 
434 | 435 |

436 | Also add information on how to contact you by electronic and paper mail. 437 |

438 | 439 |

440 | If the program is interactive, make it output a short notice like this 441 | when it starts in an interactive mode: 442 |

443 | 444 |
445 | Gnomovision version 69, Copyright (C) year name of author
446 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
447 | type `show w'.  This is free software, and you are welcome
448 | to redistribute it under certain conditions; type `show c' 
449 | for details.
450 | 
451 | 452 |

453 | The hypothetical commands `show w' and `show c' should show 454 | the appropriate parts of the General Public License. Of course, the 455 | commands you use may be called something other than `show w' and 456 | `show c'; they could even be mouse-clicks or menu items--whatever 457 | suits your program. 458 |

459 | 460 |

461 | You should also get your employer (if you work as a programmer) or your 462 | school, if any, to sign a "copyright disclaimer" for the program, if 463 | necessary. Here is a sample; alter the names: 464 |

465 | 466 | 467 |
468 | Yoyodyne, Inc., hereby disclaims all copyright
469 | interest in the program `Gnomovision'
470 | (which makes passes at compilers) written 
471 | by James Hacker.
472 | 
473 | signature of Ty Coon, 1 April 1989
474 | Ty Coon, President of Vice
475 | 
476 | 477 |

478 | This General Public License does not permit incorporating your program into 479 | proprietary programs. If your program is a subroutine library, you may 480 | consider it more useful to permit linking proprietary applications with the 481 | library. If this is what you want to do, use the 482 | GNU Lesser General Public License 483 | instead of this License. 484 |

485 | 486 | 487 | 488 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naTmeg/ScriptedAmigaEmulator/7fc423194d9690dc0f4344afe638aaf580e5e8a9/images/logo.png -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012-2016 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | -------------------------------------------------------------------------*/ 17 | 18 | a:link { color:#24c; text-decoration:none; } 19 | a:visited { color:#24c; text-decoration:none; } 20 | a:hover { color:#24c; text-decoration:underline; } 21 | 22 | body { 23 | top:0; 24 | left:0; 25 | margin:0; 26 | padding:0; 27 | font-family:Verdana; 28 | font-size:13px; 29 | background-color:#f8f8f8; 30 | -webkit-touch-callout:none; 31 | -webkit-user-select:none; 32 | -khtml-user-select:none; 33 | -moz-user-select:none; 34 | -ms-user-select:none; 35 | user-select:none; 36 | } 37 | 38 | table { 39 | white-space:nowrap; 40 | } 41 | td { 42 | text-align:left; 43 | vertical-align:top; 44 | } 45 | 46 | textarea { 47 | resize:none; 48 | font-size:13px; 49 | font-family:monospace; 50 | border-left:1px solid #ccc; 51 | border-top:1px solid #ccc; 52 | border-right:1px solid #fff; 53 | border-bottom:1px solid #fff; 54 | } 55 | 56 | input[type=text] { 57 | font-size:13px; 58 | border-left:1px solid #ccc; 59 | border-top:1px solid #ccc; 60 | border-right:1px solid #fff; 61 | border-bottom:1px solid #fff; 62 | } 63 | 64 | /*---------------------------------*/ 65 | 66 | .gray { color:gray; } 67 | .green { color:green; } 68 | .orange { color:orange; } 69 | .red { color:red; } 70 | 71 | .alt { text-align:left; vertical-align:top; } 72 | .alm { text-align:left; vertical-align:middle; } 73 | .alb { text-align:left; vertical-align:bottom; } 74 | 75 | .act { text-align:center; vertical-align:top; } 76 | .acm { text-align:center; vertical-align:middle; } 77 | .acb { text-align:center; vertical-align:bottom; } 78 | 79 | .art { text-align:right; vertical-align:top; } 80 | .arm { text-align:right; vertical-align:middle; } 81 | .arb { text-align:right; vertical-align:bottom; } 82 | 83 | .label { 84 | font-size:11px; 85 | font-weight:bold; 86 | color:#333; 87 | } 88 | .info { 89 | font-size:11px; 90 | color:dimgray; 91 | } 92 | .warn { 93 | font-size:11px; 94 | color:red; 95 | } 96 | 97 | .noscript { 98 | margin:auto; 99 | padding:4px; 100 | font-size:17px; 101 | font-weight:bold; 102 | text-align:center; 103 | color:red; 104 | } 105 | 106 | .head { 107 | width:100%; 108 | margin:auto; 109 | text-align:center; 110 | } 111 | 112 | .foot { 113 | width:100%; 114 | margin:auto; 115 | font-size:11px; 116 | text-align:center; 117 | } 118 | 119 | .linehoriz { 120 | width:100%; 121 | border-top:1px solid #bbb; 122 | } 123 | 124 | .text { 125 | border-left:1px solid #bbb; 126 | border-top:1px solid #bbb; 127 | border-right:1px solid #fff; 128 | border-bottom:1px solid #fff; 129 | background-color:#fff; 130 | -webkit-touch-callout:text; 131 | -webkit-user-select:text; 132 | -khtml-user-select:text; 133 | -moz-user-select:text; 134 | -ms-user-select:text; 135 | user-select:text; 136 | } 137 | 138 | /*---------------------------------*/ 139 | 140 | #myVideo { 141 | margin:auto; 142 | } 143 | #myVideo:-webkit-full-screen { 144 | width: 100%; 145 | height: 100%; 146 | } 147 | 148 | /*---------------------------------*/ 149 | 150 | #config select { 151 | border-left:2px solid #fff; 152 | border-top:2px solid #fff; 153 | border-right:1px solid #bbb; 154 | border-bottom:1px solid #bbb; 155 | background-color:#f8f8f8; 156 | } 157 | #config .button { 158 | width:75px; 159 | border-left:1px solid #fff; 160 | border-top:1px solid #fff; 161 | border-right:1px solid #bbb; 162 | border-bottom:1px solid #bbb; 163 | background-color:#f8f8f8; 164 | } 165 | #config .button:hover { 166 | background-color:#e8e8e8; 167 | } 168 | #config .sbutton { 169 | width:100px; 170 | padding:2px; 171 | border-left:1px solid #fff; 172 | border-top:1px solid #fff; 173 | border-right:1px solid #bbb; 174 | border-bottom:1px solid #bbb; 175 | background-color:#f8f8f8; 176 | } 177 | #config .sbutton:hover { 178 | background-color:#e8e8e8; 179 | } 180 | 181 | #config_database { 182 | width:720px; 183 | padding:10px; 184 | margin:auto; 185 | border-left:1px solid #fff; 186 | border-top:1px solid #fff; 187 | border-right:1px solid #bbb; 188 | border-bottom:1px solid #bbb; 189 | background-color:#f0f0f0; 190 | } 191 | #config_database_hof { 192 | width:75%; 193 | margin:auto; 194 | padding:10px; 195 | border-left:1px solid #bbb; 196 | border-top:1px solid #bbb; 197 | border-right:1px solid #fff; 198 | border-bottom:1px solid #fff; 199 | background-color:#e8e8e8; 200 | } 201 | #config_database_hof .ctrl { 202 | padding:2px; 203 | margin:0; 204 | border-left:1px solid #bbb; 205 | border-top:1px solid #bbb; 206 | border-right:1px solid #fff; 207 | border-bottom:1px solid #fff; 208 | background-color:#f0f0f0; 209 | } 210 | 211 | #config_advanced { 212 | width:720px; 213 | margin:auto; 214 | padding:10px; 215 | border-left:1px solid #fff; 216 | border-top:1px solid #fff; 217 | border-right:1px solid #bbb; 218 | border-bottom:1px solid #bbb; 219 | background-color:#f0f0f0; 220 | display:none; 221 | } 222 | #config_advanced table { 223 | width:100%; 224 | } 225 | 226 | #config_advanced .menu { 227 | padding:10px; 228 | border-left:1px solid #ccc; 229 | border-top:1px solid #ccc; 230 | border-right:1px solid #fff; 231 | border-bottom:1px solid #fff; 232 | background-color:#e8e8e8; 233 | } 234 | #config_advanced .menu a:link { 235 | font-size:13px; 236 | color:#000; 237 | text-decoration:none; 238 | } 239 | #config_advanced .menu a:hover { 240 | color:#888; 241 | text-decoration:none; 242 | } 243 | 244 | #config_advanced .page { 245 | padding:10px; 246 | border-left:1px solid #ccc; 247 | border-top:1px solid #ccc; 248 | border-right:1px solid #fff; 249 | border-bottom:1px solid #fff; 250 | background-color:#e8e8e8; 251 | display:none; 252 | } 253 | #cfg_page_video input[type="text"] { 254 | width:60px; 255 | } 256 | 257 | /*---------------------------------*/ 258 | 259 | #emul { 260 | display:none; 261 | } 262 | 263 | #emul button { 264 | width:75px; 265 | font-size:11px; 266 | font-weight:bold; 267 | color:#888; 268 | border:1px solid #888; 269 | background-color:#000; 270 | } 271 | #emul button:hover { 272 | color:#ccc; 273 | border:1px solid #ccc; 274 | } 275 | 276 | #controls_status { 277 | margin:auto; 278 | color:#888; 279 | font-size:9px; 280 | font-weight:bold; 281 | } 282 | 283 | #dskchg_database, #dskchg_advanced { 284 | display:none; 285 | } 286 | #dskchg_database table, #dskchg_advanced table { 287 | margin:auto; 288 | } 289 | #dskchg_database select, #dskchg_advanced select { 290 | font-size:11px; 291 | font-weight:bold; 292 | color:#888; 293 | border:1px solid #888; 294 | background-color:#000; 295 | } 296 | 297 | /*---------------------------------*/ 298 | 299 | .readme { 300 | width:1080px; 301 | margin:auto; 302 | padding:10px; 303 | border-left:1px solid #fff; 304 | border-top:1px solid #fff; 305 | border-right:1px solid #bbb; 306 | border-bottom:1px solid #bbb; 307 | background-color:#f0f0f0; 308 | } 309 | .readme table { 310 | width:100%; 311 | } 312 | .readme table td { 313 | vertical-align:top; 314 | } 315 | .readme li { 316 | padding:1px; 317 | } 318 | 319 | /*---------------------------------*/ 320 | 321 | #disass { 322 | width:720px; 323 | padding:10px; 324 | margin:auto; 325 | border-left:1px solid #fff; 326 | border-top:1px solid #fff; 327 | border-right:1px solid #bbb; 328 | border-bottom:1px solid #bbb; 329 | background-color:#f0f0f0; 330 | } 331 | #disass select { 332 | border-left:1px solid #fff; 333 | border-top:1px solid #fff; 334 | border-right:1px solid #bbb; 335 | border-bottom:1px solid #bbb; 336 | } 337 | #disass input { 338 | border-left:1px solid #bbb; 339 | border-top:1px solid #bbb; 340 | border-right:1px solid #fff; 341 | border-bottom:1px solid #fff; 342 | } 343 | 344 | #disass .button { 345 | width:100px; 346 | border-left:1px solid #fff; 347 | border-top:1px solid #fff; 348 | border-right:1px solid #bbb; 349 | border-bottom:1px solid #bbb; 350 | background-color:#f8f8f8; 351 | } 352 | #disass .button:hover { 353 | background-color:#e8e8e8; 354 | } 355 | 356 | #disass_cfg { 357 | border-left:1px solid #bbb; 358 | border-top:1px solid #bbb; 359 | border-right:1px solid #fff; 360 | border-bottom:1px solid #fff; 361 | background-color:#e8e8e8; 362 | display:none; 363 | } 364 | 365 | #disass_code { 366 | width:100%; 367 | display:none; 368 | } 369 | -------------------------------------------------------------------------------- /sae/amiga.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Notes on the global-namespace 18 | | ----------------------------- 19 | | Global object: 20 | | function SAEO_() {} 21 | | 22 | | Global error constant: 23 | | const SAEE__; 24 | | 25 | | Global constant: 26 | | const SAEC__; 27 | | 28 | | Global variable: 29 | | var SAEV__; 30 | | 31 | | Global function: 32 | | function SAEF__() {} 33 | | 34 | | Global reference (pointer) to another object: 35 | | var SAER__; 36 | | 37 | | 38 | | Tags that may appear in comments (use full-word, case-sensitive text-search) 39 | | -------------------------------- 40 | | OWN Own code added. 41 | | ATT Attention, possible source of error or problem. 42 | | FIX Need to be fixed or implemented. 43 | | OPT Need or can be optimized. 44 | | ORG Original code, e.g. something required to be disabled. 45 | | SECT Code section. 46 | -------------------------------------------------------------------------*/ 47 | 48 | const SAEC_Version = 0; 49 | const SAEC_Revision = 9; 50 | const SAEC_Patch = 13; 51 | 52 | /*---------------------------------*/ 53 | /* errors */ 54 | 55 | function SAEO_Error(err, msg) { 56 | this.err = err; 57 | this.msg = msg; 58 | } 59 | SAEO_Error.prototype = new Error; 60 | 61 | const SAEE_None = 0; 62 | 63 | const SAEE_AlreadyRunning = 1; 64 | const SAEE_NotRunning = 2; 65 | const SAEE_NoTimer = 3; 66 | const SAEE_NoMemory = 4; 67 | const SAEE_Assert = 5; 68 | const SAEE_Internal = 6; 69 | 70 | const SAEE_Config_Invalid = 10; 71 | const SAEE_Config_Compressed = 11; 72 | 73 | const SAEE_CPU_Internal = 20; 74 | const SAEE_CPU_Requires68020 = 21; 75 | const SAEE_CPU_Requires680EC20 = 22; 76 | const SAEE_CPU_Requires68030 = 23; 77 | const SAEE_CPU_Requires68040 = 24; 78 | 79 | const SAEE_Memory_NoKickstartRom = 30; 80 | const SAEE_Memory_NoExtendedRom = 31; 81 | const SAEE_Memory_RomSize = 32; 82 | const SAEE_Memory_RomKey = 33; 83 | const SAEE_Memory_RomDecode = 34; 84 | const SAEE_Memory_RomChecksum = 35; 85 | const SAEE_Memory_RomUnknown = 36; 86 | 87 | const SAEE_Video_ElementNotFound = 40; 88 | const SAEE_Video_RequiresCanvas = 41; 89 | const SAEE_Video_RequiresWegGl = 42; 90 | const SAEE_Video_ComphileShader = 43; 91 | const SAEE_Video_LinkShader = 44; 92 | const SAEE_Video_RequiresFullscreen = 45; 93 | 94 | const SAEE_Audio_RequiresWebAudio = 50; 95 | 96 | const SAEE_Input_GamepadNotReady = 60; 97 | 98 | /*-----------------------------------------------------------------------*/ 99 | /* global references */ 100 | 101 | var SAER = null; 102 | 103 | /*---------------------------------*/ 104 | /* global constants */ 105 | 106 | const SAEC_spcflag_STOP = 2; 107 | const SAEC_spcflag_COPPER = 4; 108 | const SAEC_spcflag_INT = 8; 109 | const SAEC_spcflag_BRK = 16; 110 | //const SAEC_spcflag_UAEINT = 32; 111 | const SAEC_spcflag_TRACE = 64; 112 | const SAEC_spcflag_DOTRACE = 128; 113 | const SAEC_spcflag_DOINT = 256; 114 | const SAEC_spcflag_BLTNASTY = 512; 115 | //const SAEC_spcflag_EXEC = 1024; 116 | //const SAEC_spcflag_ACTION_REPLAY = 2048; 117 | //const SAEC_spcflag_TRAP = 4096; /* enforcer-hack */ 118 | const SAEC_spcflag_MODE_CHANGE = 8192; 119 | const SAEC_spcflag_CHECK = 32768; 120 | 121 | const SAEC_command_Quit = 1; 122 | const SAEC_command_Reset = 2; 123 | const SAEC_command_KeyboardReset = 3; 124 | const SAEC_command_HardReset = 4; 125 | const SAEC_command_Pause = 5; 126 | const SAEC_command_Resume = 6; 127 | 128 | /*---------------------------------*/ 129 | 130 | const SAEC_Info_Brower_ID_Unknown = 0; 131 | const SAEC_Info_Brower_ID_Chrome = 1; 132 | const SAEC_Info_Brower_ID_Safari = 2; 133 | const SAEC_Info_Brower_ID_Opera = 3; 134 | const SAEC_Info_Brower_ID_Firefox = 4; 135 | const SAEC_Info_Brower_ID_InternetExplorer = 5; 136 | 137 | const SAEC_info = (function() { 138 | var info = { 139 | browser: { 140 | id: SAEC_Info_Brower_ID_Unknown, 141 | name: "Unknown", 142 | plat: "Unknown", 143 | lang: "en" 144 | }, 145 | memory: { 146 | maxSize: 0 147 | }, 148 | audio: { 149 | webAudio: false 150 | }, 151 | video: { 152 | canvas: false, 153 | webGL: false, 154 | pointerLock: false, 155 | requestFullScreen: false 156 | }, 157 | input: { 158 | gamepad: false 159 | } 160 | }; 161 | 162 | /* browser */ 163 | if (navigator.userAgent.indexOf("Chrome") > -1) { 164 | info.browser.id = SAEC_Info_Brower_ID_Chrome; 165 | info.browser.name = "Google Chrome"; 166 | } 167 | else if (navigator.userAgent.indexOf("Safari") > -1) { 168 | info.browser.id = SAEC_Info_Brower_ID_Safari; 169 | info.browser.name = "Apple Safari"; 170 | } 171 | else if (navigator.userAgent.indexOf("Opera") > -1) { 172 | info.browser.id = SAEC_Info_Brower_ID_Opera; 173 | info.browser.name = "Opera"; 174 | } 175 | else if (navigator.userAgent.indexOf("Firefox") > -1) { 176 | info.browser.id = SAEC_Info_Brower_ID_Firefox; 177 | info.browser.name = "Mozilla Firefox"; 178 | } 179 | else if (navigator.userAgent.indexOf("MSIE") > -1) { 180 | info.browser.id = SAEC_Info_Brower_ID_InternetExplorer; 181 | info.browser.name = "Microsoft Internet Explorer"; 182 | } 183 | info.browser.plat = navigator.platform; 184 | info.browser.lang = navigator.language; 185 | 186 | /* max memory */ 187 | if (0) { 188 | var size = 1048576; 189 | while (true) { 190 | try { 191 | var data = new Uint8Array(size); 192 | delete data; 193 | info.memory.maxSize = size; 194 | } catch (e) { 195 | break; 196 | } 197 | size *= 2; 198 | } 199 | } else 200 | info.memory.maxSize = 1073741824; //1G 201 | 202 | /* audio */ 203 | if (typeof window.webkitAudioContext != "undefined" || typeof window.AudioContext != "undefined" ) 204 | info.audio.webAudio = true; 205 | /* disabled because audioContextDriver() does require user-initiation 206 | var audioContext = null; 207 | try { 208 | var audioContextDriver = window.AudioContext || window.webkitAudioContext; 209 | audioContext = new audioContextDriver(); 210 | var audioProcessor = audioContext.createScriptProcessor(1024, 2, 2); 211 | 212 | info.audio.webAudio = true; 213 | 214 | if (audioContext.close) audioContext.close().then(function() {}); 215 | audioContext = null; 216 | } catch (e) { 217 | if (audioContext) { 218 | if (audioContext.close) audioContext.close().then(function() {}); 219 | audioContext = null; 220 | } 221 | }*/ 222 | 223 | /* video */ 224 | var canvas = document.createElement("canvas"); 225 | if (canvas && canvas.getContext) { 226 | try { 227 | var ctx = canvas.getContext("2d"); 228 | var imageData = ctx.createImageData(16, 16); 229 | info.video.canvas = true; 230 | 231 | try { 232 | const glParams = { 233 | alpha: false, 234 | depth: true, 235 | stencil: false, 236 | antialias: false, 237 | premultipliedAlpha: false, 238 | preserveDrawingBuffer: true, 239 | failIfMajorPerformanceCaveat: false 240 | }; 241 | ctx = canvas.getContext("webgl", glParams) || canvas.getContext("experimental-webgl", glParams); 242 | info.video.webGL = true; 243 | } catch(e) {} 244 | 245 | /* pointerLock API */ 246 | if (canvas.webkitRequestPointerLock !== undefined || 247 | canvas.mozRequestPointerLock !== undefined || 248 | canvas.msRequestPointerLock !== undefined || 249 | canvas.requestPointerLock !== undefined 250 | ) info.video.pointerLock = true; 251 | 252 | /* fullScreen API */ 253 | if (canvas.webkitRequestFullscreen !== undefined || 254 | canvas.mozRequestFullScreen !== undefined || 255 | canvas.msRequestFullscreen !== undefined || 256 | canvas.requestFullScreen !== undefined 257 | ) info.video.requestFullScreen = true; 258 | } catch(e) {} 259 | } 260 | 261 | /* gamepad API */ 262 | if (navigator.webkitGetGamepads !== undefined || navigator.getGamepads !== undefined) 263 | info.input.gamepad = true; 264 | 265 | return info; 266 | })(); 267 | 268 | /*---------------------------------*/ 269 | /* global variables */ 270 | 271 | var SAEV_spcflags = 0; 272 | var SAEV_command = 0; 273 | 274 | /*---------------------------------*/ 275 | /* global functions */ 276 | 277 | function SAEF_setSpcFlags(x) { SAEV_spcflags |= x; }; 278 | function SAEF_clrSpcFlags(x) { SAEV_spcflags &= ~x; }; 279 | 280 | /*---------------------------------*/ 281 | 282 | function SAEF_now() { 283 | return Math.floor(performance.now() * 1000); /* micro-seconds since page-load */ 284 | } 285 | function SAEF_sleep(ms) { 286 | var start = performance.now(); 287 | while ((performance.now() - start) < ms) {} /* pretty nasty */ 288 | } 289 | 290 | /*---------------------------------*/ 291 | /* debug */ 292 | 293 | function SAEF_log() { 294 | if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Log && arguments.length) { 295 | var str = sprintf.apply(this, arguments); 296 | if (console.log) console.log(str); 297 | } 298 | } 299 | function SAEF_info() { 300 | if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Info && arguments.length) { 301 | var str = sprintf.apply(this, arguments); 302 | if (console.info) console.info(str); 303 | } 304 | } 305 | function SAEF_warn() { 306 | if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Warn && arguments.length) { 307 | var str = sprintf.apply(this, arguments); 308 | if (console.warn) console.warn(str); 309 | 310 | } 311 | } 312 | function SAEF_error() { 313 | if (SAEV_config.debug.level >= SAEC_Config_Debug_Level_Error && arguments.length) { 314 | var str = sprintf.apply(this, arguments); 315 | if (console.error) console.error(str); 316 | } 317 | } 318 | function SAEF_fatal() { 319 | var argumentsArray = Array.prototype.slice.call(arguments); 320 | var err = argumentsArray[0]; 321 | var str = sprintf.apply(this, argumentsArray.slice(1)); 322 | if (console.error) console.error(str); 323 | throw new SAEO_Error(err, str); 324 | } 325 | 326 | function SAEF_assert(cond) { 327 | if (!cond) { 328 | var err = SAEE_Assert; 329 | var str = "Assertion failed. This is a bug in SAE."; 330 | if (console.error) console.error(str); 331 | throw new SAEO_Error(err, str); 332 | } 333 | } 334 | 335 | /*---------------------------------*/ 336 | 337 | function ScriptedAmigaEmulator() { 338 | SAER = this; 339 | 340 | this.audio = new SAEO_Audio(); 341 | this.autoconf = new SAEO_AutoConf(); 342 | this.blitter = new SAEO_Blitter(); 343 | this.cia = new SAEO_CIA(); 344 | this.config = new SAEO_Configuration(); 345 | this.copper = new SAEO_Copper(); 346 | this.cpu = new SAEO_CPU(); 347 | this.custom = new SAEO_Custom(); 348 | this.devices = new SAEO_Devices(); 349 | this.disk = new SAEO_Disk(); 350 | this.dongle = new SAEO_Dongle(); 351 | this.events = new SAEO_Events(); 352 | this.expansion = new SAEO_Expansion(); 353 | this.filesys = new SAEO_Filesys(); 354 | this.gayle = new SAEO_Gayle(); 355 | this.gui = new SAEO_GUI(); 356 | this.hardfile = new SAEO_Hardfile(); 357 | this.ide = new SAEO_IDE(); 358 | this.input = new SAEO_Input(); 359 | this.m68k = new SAEO_M68K(); 360 | this.memory = new SAEO_Memory(); 361 | this.parallel = new SAEO_Parallel(); 362 | this.playfield = new SAEO_Playfield(); 363 | this.roms = new SAEO_Roms(); 364 | this.rtc = new SAEO_RTC(); 365 | this.serial = new SAEO_Serial(); 366 | this.video = new SAEO_Video(); 367 | 368 | /*---------------------------------*/ 369 | 370 | this.running = false; 371 | this.paused = false; 372 | 373 | /*-----------------------------------------------------------------------*/ 374 | 375 | this.dump = function () { 376 | this.m68k.dump(); 377 | //this.memory.dump(); 378 | //this.cia.dump(); 379 | }; 380 | 381 | /*-----------------------------------------------------------------------*/ 382 | 383 | this.do_start_program = function() { 384 | if (SAEV_command >= 0) 385 | SAEV_command = SAEC_command_Reset; 386 | 387 | this.m68k.m68k_go(true); 388 | } 389 | 390 | this.do_leave_program = function() { 391 | //sampler_free(); 392 | this.video.cleanup(); 393 | this.input.cleanup(); 394 | this.disk.cleanup(); 395 | this.audio.cleanup(); 396 | //dump_counts(); 397 | this.serial.cleanup(); 398 | /*#ifdef CDTV 399 | cdtv_free(); 400 | cdtvcr_free(); 401 | #endif 402 | #ifdef CD32 403 | akiko_free(); 404 | cd32_fmv_free(); 405 | #endif*/ 406 | //this.gui.cleanup(); //empty 407 | //#ifdef AUTOCONFIG 408 | this.expansion.cleanup(); 409 | //#endif 410 | //#ifdef FILESYS 411 | this.filesys.cleanup(); 412 | //#endif 413 | this.gayle.cleanup(); 414 | /*idecontroller_free(); 415 | device_func_reset(); 416 | #ifdef WITH_TOCCATA 417 | sndboard_free(); 418 | #endif*/ 419 | this.memory.cleanup(); 420 | //free_shm(); 421 | this.autoconf.cleanup(); 422 | } 423 | 424 | this.start_program = function() { 425 | this.do_start_program(); 426 | 427 | if (typeof SAEV_config.hook.event.started === "function") 428 | SAEV_config.hook.event.started(); 429 | } 430 | 431 | this.leave_program = function() { 432 | this.dump(); 433 | this.do_leave_program(); 434 | 435 | if (typeof SAEV_config.hook.event.stopped === "function") 436 | SAEV_config.hook.event.stopped(); 437 | } 438 | 439 | this.pause_program = function(p) { 440 | this.audio.pauseResume(p); 441 | this.events.pauseResume(p); 442 | 443 | if (typeof SAEV_config.hook.event.paused === "function") 444 | SAEV_config.hook.event.paused(p); 445 | } 446 | 447 | /*---------------------------------*/ 448 | /* API */ 449 | 450 | this.getVersion = function(str) { 451 | if (str) 452 | return sprintf("%d.%d.%d", SAEC_Version, SAEC_Revision, SAEC_Patch); 453 | else 454 | return [SAEC_Version, SAEC_Revision, SAEC_Patch]; 455 | } 456 | this.getInfo = function() { 457 | return SAEC_info; 458 | } 459 | this.getConfig = function() { 460 | return SAEV_config; 461 | } 462 | 463 | this.setDefaults = function() { 464 | return this.config.setDefaults(); 465 | } 466 | this.setModel = function(model, config) { 467 | return this.config.setModel(model, config); 468 | } 469 | 470 | this.setMountInfoDefaults = function(num) { 471 | var ci = SAEV_config.mount.config[num].ci; 472 | this.filesys.uci_set_defaults(ci, false); 473 | } 474 | 475 | this.start = function() { 476 | if (SAER.running) { 477 | SAEF_warn("sae.start() emulation already running"); 478 | return SAEE_AlreadyRunning; 479 | } 480 | SAEF_info("sae.start() starting..."); 481 | 482 | var err; 483 | if ((err = this.config.setup()) != SAEE_None) 484 | return err; 485 | if ((err = this.video.obtain()) != SAEE_None) 486 | return err; 487 | if ((err = this.audio.obtain()) != SAEE_None) 488 | return err; 489 | if ((err = this.input.setup()) != SAEE_None) //inputdevice_init(); 490 | return err; 491 | if ((err = this.gui.setup()) != SAEE_None) 492 | return err; 493 | 494 | /*#ifdef PICASSO96 495 | picasso_reset(); 496 | #endif*/ 497 | 498 | //this.config.fixup_prefs(currprefs, true); 499 | //SAEV_config.audio.mode = 0; /* force sound settings change */ 500 | 501 | this.memory.hardreset(2); 502 | if ((err = this.memory.reset(true)) == SAEE_None) 503 | { 504 | /*#ifdef AUTOCONFIG 505 | native2amiga_install(); 506 | #endif*/ 507 | this.custom.setup(); //OWN 508 | this.blitter.setup(); //OWN 509 | this.playfield.setup(); //custom_init(); 510 | //this.serial.setup(); //empty 511 | this.disk.setup(); 512 | 513 | this.events.reset_frame_rate_hack(); 514 | if ((err = this.m68k.setup()) == SAEE_None) /* m68k_init() must come after reset_frame_rate_hack() */ 515 | { 516 | //this.gui.update(); //empty 517 | if ((err = this.video.setup(true)) == SAEE_None) 518 | { 519 | if ((err = this.audio.setup()) == SAEE_None) 520 | { 521 | this.start_program(); 522 | SAEF_info("sae.start() ...done"); 523 | return SAEE_None; 524 | } 525 | this.video.cleanup(); 526 | } 527 | } 528 | } 529 | this.input.cleanup(); 530 | SAEF_error("sae.start() ...error %d", err); 531 | return err; 532 | } 533 | 534 | this.stop = function() { //uae_quit() 535 | if (this.running) { 536 | SAEF_info("sae.stop()"); 537 | if (SAEV_command != -SAEC_command_Quit) 538 | SAEV_command = -SAEC_command_Quit; 539 | 540 | return SAEE_None; 541 | } else { 542 | SAEF_warn("sae.stop() emulation not running"); 543 | return SAEE_NotRunning; 544 | } 545 | }; 546 | 547 | this.reset = function(hard, keyboard) { //uae_reset(hard, keyboard) 548 | if (typeof hard == "undefined") var hard = false; 549 | if (typeof keyboard == "undefined") var keyboard = false; 550 | if (this.running) { 551 | SAEF_info("sae.reset() hard %d, keyboard %d", hard?1:0, keyboard?1:0); 552 | if (SAEV_command == 0) { 553 | SAEV_command = -SAEC_command_Reset; /* soft */ 554 | if (keyboard) 555 | SAEV_command = -SAEC_command_KeyboardReset; 556 | if (hard) 557 | SAEV_command = -SAEC_command_HardReset; 558 | } 559 | return SAEE_None; 560 | } else { 561 | SAEF_warn("sae.reset() emulation not running"); 562 | return SAEE_NotRunning; 563 | } 564 | }; 565 | 566 | this.pause = function(pause) { 567 | if (this.running) { 568 | if (!this.paused && pause) { 569 | SAEF_info("sae.pause() pausing emulation"); 570 | SAEV_command = SAEC_command_Pause; 571 | } 572 | else if (this.paused && !pause) { 573 | SAEF_info("sae.pause() resuming emulation"); 574 | SAEV_command = SAEC_command_Resume; 575 | } 576 | return SAEE_None; 577 | } else { 578 | SAEF_warn("sae.pause() emulation not running"); 579 | return SAEE_NotRunning; 580 | } 581 | }; 582 | 583 | this.mute = function(mute) { 584 | if (this.running) { 585 | this.audio.mute(mute); 586 | if (mute) 587 | SAEF_log("sae.mute() audio muted"); 588 | else 589 | SAEF_log("sae.mute() playing audio"); 590 | return SAEE_None; 591 | } else { 592 | SAEF_warn("sae.mute() emulation not running"); 593 | return SAEE_NotRunning; 594 | } 595 | }; 596 | 597 | this.screen = function(screen) { 598 | if (this.running) { 599 | if (SAEC_info.video.requestFullScreen) { 600 | this.video.screen(screen); 601 | if (screen) 602 | SAEF_log("sae.screen() screen-mode"); 603 | else 604 | SAEF_log("sae.screen() window-mode"); 605 | return SAEE_None; 606 | } else { 607 | SAEF_error("sae.screen() screen-api not supported"); 608 | return SAEE_Video_RequiresFullscreen; 609 | } 610 | } else { 611 | SAEF_warn("sae.screen() emulation not running"); 612 | return SAEE_NotRunning; 613 | } 614 | }; 615 | 616 | this.insert = function(unit) { 617 | if (this.running) { 618 | var file = SAEV_config.floppy.drive[unit].file; 619 | this.disk.insert(unit, file); 620 | SAEF_info("sae.insert() unit %d inserted, name '%s', size %d, protected %d", unit, file.name, file.size, file.prot?1:0); 621 | return SAEE_None; 622 | } else { 623 | SAEF_warn("sae.insert() emulation not running"); 624 | return SAEE_NotRunning; 625 | } 626 | }; 627 | 628 | this.eject = function(unit) { 629 | if (this.running) { 630 | this.disk.eject(unit); 631 | SAEF_info("sae.eject() unit %d ejected", unit); 632 | return SAEE_None; 633 | } else { 634 | SAEF_warn("sae.eject() emulation not running"); 635 | return SAEE_NotRunning; 636 | } 637 | }; 638 | 639 | this.getRomInfo = function(ri, file) { 640 | return this.roms.examine(ri, file); 641 | }; 642 | 643 | this.getDiskInfo = function(di, unit) { 644 | return this.disk.examine(di, unit); 645 | }; 646 | 647 | this.createDisk = function(unit, name, mode, type, label, ffs, bootable) { 648 | if (!this.disk.create(unit, name, mode, type, label, ffs, bootable)) 649 | return SAEE_NoMemory; 650 | return SAEE_None; 651 | }; 652 | 653 | this.keyPress = function(e, down) { 654 | this.input.keyPress(e, down); 655 | return SAEE_None; 656 | }; 657 | 658 | /*---------------------------------*/ 659 | 660 | SAEF_info("SAE %d.%d.%d", SAEC_Version, SAEC_Revision, SAEC_Patch); 661 | } 662 | -------------------------------------------------------------------------------- /sae/autoconf.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | /* global references */ 20 | 21 | var SAER_AutoConf_bank = null; 22 | 23 | /*---------------------------------*/ 24 | /* global constants */ 25 | 26 | const SAEC_AutoConf_RTS = 0x4e75; 27 | //const SAEC_AutoConf_RTE = 0x4e73; 28 | 29 | /*---------------------------------*/ 30 | /* global variables */ 31 | 32 | var SAEV_AutoConf_base = 0xf00000; //RTAREA_DEFAULT; 33 | 34 | var SAEV_AutoConf_boot_rom_type = 0; 35 | var SAEV_AutoConf_boot_rom_size = 0; 36 | 37 | /*---------------------------------*/ 38 | 39 | function SAEO_AutoConf() { 40 | const RTAREA_DEFAULT = 0xf00000; 41 | const RTAREA_BACKUP = 0xef0000; 42 | const RTAREA_BACKUP_2 = 0xdb0000; 43 | const RTAREA_SIZE = 0x10000; 44 | 45 | const RTAREA_TRAPS = 0x3000; 46 | const RTAREA_RTG = 0x3800; 47 | const RTAREA_TRAMPOLINE = 0x3b00; 48 | const RTAREA_DATAREGION = 0xF000; 49 | 50 | const RTAREA_FSBOARD = 0xFFEC; 51 | const RTAREA_HEARTBEAT = 0xFFF0; 52 | const RTAREA_TRAPTASK = 0xFFF4; 53 | const RTAREA_EXTERTASK = 0xFFF8; 54 | const RTAREA_INTREQ = 0xFFFC; 55 | 56 | const RTAREA_TRAP_DATA = 0x4000; 57 | const RTAREA_TRAP_DATA_SIZE = 0x8000; 58 | const RTAREA_TRAP_DATA_SLOT_SIZE = 0x2000; // 8192 59 | const RTAREA_TRAP_DATA_SECOND = 80; 60 | const RTAREA_TRAP_DATA_TASKWAIT = (RTAREA_TRAP_DATA_SECOND - 4); 61 | const RTAREA_TRAP_DATA_EXTRA = 144; 62 | const RTAREA_TRAP_DATA_EXTRA_SIZE = (RTAREA_TRAP_DATA_SLOT_SIZE - RTAREA_TRAP_DATA_EXTRA); 63 | 64 | const RTAREA_TRAP_SEND_DATA = 0xc0000; 65 | const RTAREA_TRAP_SEND_DATA_SIZE = 0x2000; 66 | 67 | const RTAREA_TRAP_STATUS = 0xF000; 68 | const RTAREA_TRAP_STATUS_SIZE = 8; 69 | const RTAREA_TRAP_STATUS_SECOND = 4; 70 | 71 | const RTAREA_TRAP_SEND_STATUS = 0xF100; 72 | 73 | const RTAREA_SYSBASE = 0x3FFC; 74 | 75 | const RTAREA_TRAP_DATA_NUM = (RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE); 76 | 77 | 78 | 79 | /* Commonly used autoconfig strings */ 80 | //var EXPANSION_explibname = 0, EXPANSION_doslibname = 0, EXPANSION_uaeversion = 0; 81 | //var EXPANSION_uaedevname, EXPANSION_explibbase = 0; 82 | //var EXPANSION_bootcode = 0, EXPANSION_nullfunc = 0; 83 | 84 | /* ROM tag area memory access */ 85 | //var rtarea_base = RTAREA_DEFAULT; --> SAEV_AutoConf_base 86 | var hardware_trap_event = new Array(RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE); //HANDLE 87 | 88 | var rt_trampoline_ptr = 0, trap_entry = 0; 89 | //var hwtrap_waiting = 0; //extern volatile uae_atomic, in traps 90 | var filesystem_state = 0; //extern int 91 | 92 | //var uae_boot_rom_type = 0; -> SAEV_AutoConf_boot_rom_type 93 | //var uae_boot_rom_size = 0; -> SAEV_AutoConf_boot_rom_size 94 | 95 | var uae_int_requested = 0; //volatile uae_atomic 96 | 97 | /*-----------------------------------------------------------------------*/ 98 | 99 | function check_boot_rom(p) { 100 | var b = RTAREA_DEFAULT; 101 | 102 | /*if (currprefs.uaeboard > 1) { 103 | p.type = 2; 104 | return 0x00eb0000; // fixme! 105 | } 106 | p.type = 0; 107 | if (currprefs.boot_rom == 1) 108 | return 0;*/ 109 | p.type = 1; 110 | /*if (currprefs.cs_cdtvcd || currprefs.cs_cdtvscsi || currprefs.uae_hide > 1) 111 | b = RTAREA_BACKUP;*/ 112 | if (SAEV_config.chipset.mbdmac == 1)// || currprefs.cpuboard_type) 113 | b = RTAREA_BACKUP; 114 | // CSPPC enables MMU at boot and remaps 0xea0000->0xeffff. 115 | /*if (ISCPUBOARD(BOARD_BLIZZARD, BOARD_BLIZZARD_SUB_PPC)) 116 | b = RTAREA_BACKUP_2;*/ 117 | var ab = SAER_Memory_getBank(RTAREA_DEFAULT); 118 | if (ab !== null) { 119 | if (SAER_Memory_check(RTAREA_DEFAULT, 65536)) 120 | b = RTAREA_BACKUP; 121 | } 122 | /*if (nr_directory_units(NULL)) 123 | return b; 124 | if (nr_directory_units(&currprefs)) 125 | return b; 126 | if (currprefs.socket_emu) 127 | return b; 128 | if (currprefs.uaeserial) 129 | return b; 130 | if (currprefs.scsi == 1) //uaescsi.device 131 | return b; 132 | if (currprefs.sana2) 133 | return b; 134 | if (currprefs.input_tablet > 0) 135 | return b; 136 | if (currprefs.rtgmem_size && currprefs.rtgmem_type < GFXBOARD_HARDWARE) 137 | return b; 138 | if (currprefs.win32_automount_removable) 139 | return b;*/ 140 | if (SAEV_config.memory.chipSize > 2 * 1024 * 1024) 141 | return b; 142 | /*if (currprefs.z3chipmem_size) 143 | return b; 144 | if (currprefs.boot_rom >= 3) 145 | return b; 146 | if (currprefs.boot_rom == 2 && b == 0xf00000) { 147 | p.type = -1; 148 | return b; 149 | }*/ 150 | p.type = 0; 151 | return 0; 152 | } 153 | 154 | this.need_uae_boot_rom = function() { 155 | var p = { type:0 }; 156 | var v = check_boot_rom(p); 157 | SAEV_AutoConf_boot_rom_type = p.type; 158 | SAEF_log("autoconf.need_uae_boot_rom() type %d", SAEV_AutoConf_boot_rom_type); 159 | if (!SAEV_AutoConf_base) { 160 | v = 0; 161 | SAEV_AutoConf_boot_rom_type = 0; 162 | } 163 | return v; 164 | } 165 | 166 | /*-----------------------------------------------------------------------*/ 167 | 168 | /*static bool istrapwait(void) { 169 | for (int i = 0; i < RTAREA_TRAP_DATA_NUM; i++) { 170 | uae_u8 *data = rtarea_bank.baseaddr + RTAREA_TRAP_DATA + i * RTAREA_TRAP_DATA_SLOT_SIZE; 171 | uae_u8 *status = rtarea_bank.baseaddr + RTAREA_TRAP_STATUS + i * RTAREA_TRAP_STATUS_SIZE; 172 | if (get_long_host(data + RTAREA_TRAP_DATA_TASKWAIT) && status[3] && status[2] >= 0x80) { 173 | return true; 174 | } 175 | } 176 | return false; 177 | }*/ 178 | this.rethink_traps = function() { 179 | return false; 180 | /*if (currprefs.uaeboard < 2) 181 | return false; 182 | if (istrapwait()) { 183 | atomic_or(&uae_int_requested, 0x4000); 184 | set_special_exter(SPCFLAG_UAEINT); 185 | return true; 186 | } 187 | atomic_and(&uae_int_requested, ~0x4000); 188 | return false;*/ 189 | } 190 | 191 | /*-----------------------------------------------------------------------*/ 192 | 193 | const RTAREA_WRITEOFFSET = 0xfff0; 194 | 195 | function hwtrap_check_int() { 196 | if (hwtrap_waiting == 0) { 197 | atomic_and(uae_int_requested, ~0x2000); 198 | } else { 199 | atomic_or(uae_int_requested, 0x2000); 200 | set_special_exter(SPCFLAG_UAEINT); 201 | } 202 | } 203 | 204 | function rtarea_trap_data(addr) { 205 | if (addr >= RTAREA_TRAP_DATA && addr < RTAREA_TRAP_DATA + RTAREA_TRAP_DATA_SIZE) 206 | return true; 207 | return false; 208 | } 209 | 210 | function rtarea_trap_status(addr) { 211 | if (addr >= RTAREA_TRAP_STATUS && addr < RTAREA_TRAP_STATUS + RTAREA_TRAP_DATA_NUM * RTAREA_TRAP_STATUS_SIZE) 212 | return true; 213 | return false; 214 | } 215 | 216 | /*---------------------------------*/ 217 | 218 | function rtarea_get32(addr) { 219 | addr &= 0xFFFF; 220 | return ((rtarea_bank.baseaddr[addr] << 24) | (rtarea_bank.baseaddr[addr + 1] << 16) | (rtarea_bank.baseaddr[addr + 2] << 8) | rtarea_bank.baseaddr[addr + 3]) >>> 0; 221 | } 222 | function rtarea_get16(addr) { 223 | addr &= 0xFFFF; 224 | return (rtarea_bank.baseaddr[addr] << 8) + rtarea_bank.baseaddr[addr + 1]; 225 | } 226 | function rtarea_get8(addr) { 227 | addr &= 0xFFFF; 228 | 229 | if (rtarea_trap_status(addr)) { 230 | var addr2 = addr - RTAREA_TRAP_STATUS; 231 | var trap_offset = addr2 & (RTAREA_TRAP_STATUS_SIZE - 1); 232 | var trap_slot = Math.floor(addr2 / RTAREA_TRAP_STATUS_SIZE); 233 | if (trap_offset == 0) { 234 | // 0 = busy wait, 1 = Wait() 235 | rtarea_bank.baseaddr[addr] = filesystem_state ? 1 : 0; 236 | } 237 | } else if (addr == RTAREA_INTREQ + 0) { 238 | rtarea_bank.baseaddr[addr] = atomic_bit_test_and_reset(uae_int_requested, 0); 239 | //SAEF_log("autoconf.rtarea_get8() %s", rtarea_bank.baseaddr[addr] ? "+" : "-"); 240 | } else if (addr == RTAREA_INTREQ + 1) { 241 | rtarea_bank.baseaddr[addr] = hwtrap_waiting != 0; 242 | } else if (addr == RTAREA_INTREQ + 2) { 243 | /*if (SAER.autoconf.rethink_traps()) //OWN empty 244 | rtarea_bank.baseaddr[addr] = 1; 245 | else*/ 246 | rtarea_bank.baseaddr[addr] = 0; 247 | } 248 | hwtrap_check_int(); 249 | return rtarea_bank.baseaddr[addr]; 250 | } 251 | 252 | function rtarea_write(addr) { 253 | if (addr >= RTAREA_WRITEOFFSET) 254 | return true; 255 | if (addr >= RTAREA_SYSBASE && addr < RTAREA_SYSBASE + 4) 256 | return true; 257 | return rtarea_trap_data(addr) || rtarea_trap_status(addr); 258 | } 259 | function rtarea_put8(addr, value) { 260 | addr &= 0xffff; 261 | if (!rtarea_write(addr)) 262 | return; 263 | rtarea_bank.baseaddr[addr] = value; 264 | if (!rtarea_trap_status(addr)) 265 | return; 266 | addr -= RTAREA_TRAP_STATUS; 267 | var trap_offset = addr & (RTAREA_TRAP_STATUS_SIZE - 1); 268 | var trap_slot = Math.floor(addr / RTAREA_TRAP_STATUS_SIZE); 269 | if (trap_offset == RTAREA_TRAP_STATUS_SECOND + 3) { 270 | var v = value; 271 | if (v != 0xff && v != 0xfe && v != 0x01 && v != 02) 272 | SAEF_log("autoconf.rtarea_put8() TRAP %d (%02x)", trap_slot, v); 273 | if (v == 0xfe) 274 | atomic_dec(hwtrap_waiting); 275 | if (v == 0x01) 276 | atomic_dec(hwtrap_waiting); 277 | if (v == 0x01 || v == 0x02) { 278 | // signal call_hardware_trap_back() 279 | // FIXME: OS specific code! 280 | SetEvent(hardware_trap_event[trap_slot]); 281 | } 282 | } 283 | } 284 | function rtarea_put16(addr, value) { 285 | addr &= 0xffff; 286 | value &= 0xffff; 287 | if (!rtarea_write(addr)) 288 | return; 289 | rtarea_put8(addr, value >> 8); 290 | rtarea_put8(addr + 1, value & 0xff); 291 | if (!rtarea_trap_status(addr)) 292 | return; 293 | addr -= RTAREA_TRAP_STATUS; 294 | var trap_offset = addr & (RTAREA_TRAP_STATUS_SIZE - 1); 295 | var trap_slot = Math.floor(addr / RTAREA_TRAP_STATUS_SIZE); 296 | if (trap_offset == 0) { 297 | SAEF_log("autoconf.rtarea_put16() TRAP %d (%04x)", trap_slot, value); 298 | call_hardware_trap(rtarea_bank.baseaddr, SAEV_AutoConf_base, trap_slot); 299 | } 300 | } 301 | function rtarea_put32(addr, value) { 302 | addr &= 0xffff; 303 | if (!rtarea_write(addr)) 304 | return; 305 | rtarea_bank.baseaddr[addr + 0] = value >>> 24; 306 | rtarea_bank.baseaddr[addr + 1] = (value >>> 16) & 0xff; 307 | rtarea_bank.baseaddr[addr + 2] = (value >>> 8) & 0xff; 308 | rtarea_bank.baseaddr[addr + 3] = value & 0xff; 309 | } 310 | 311 | function rtarea_xlate(addr) { 312 | addr &= 0xFFFF; 313 | //return rtarea_bank.baseaddr + addr; 314 | return addr; 315 | } 316 | function rtarea_check(addr, size) { 317 | addr &= 0xFFFF; 318 | return (addr + size) <= 0xFFFF; 319 | } 320 | 321 | var rtarea_bank = new SAEO_Memory_addrbank( 322 | rtarea_get32, rtarea_get16, rtarea_get8, 323 | rtarea_put32, rtarea_put16, rtarea_put8, 324 | rtarea_xlate, rtarea_check, null, "rtarea", "UAE Boot ROM", 325 | rtarea_get32, rtarea_get16, 326 | SAEC_Memory_addrbank_flag_ROMIN | SAEC_Memory_addrbank_flag_PPCIOSPACE//, S_READ, S_WRITE 327 | ); 328 | SAER_AutoConf_bank = rtarea_bank; 329 | 330 | /*-----------------------------------------------------------------------*/ 331 | 332 | this.reset = function() { //rtarea_reset() 333 | //memset(rtarea_bank.baseaddr + RTAREA_TRAP_DATA, 0, RTAREA_TRAP_DATA_SIZE); 334 | //memset(rtarea_bank.baseaddr + RTAREA_TRAP_STATUS, 0, RTAREA_TRAP_STATUS_SIZE * RTAREA_TRAP_DATA_NUM); 335 | SAEF_memset(rtarea_bank.baseaddr,RTAREA_TRAP_DATA, 0, RTAREA_TRAP_DATA_SIZE); 336 | SAEF_memset(rtarea_bank.baseaddr,RTAREA_TRAP_STATUS, 0, RTAREA_TRAP_STATUS_SIZE * RTAREA_TRAP_DATA_NUM); 337 | } 338 | 339 | /*-----------------------------------------------------------------------*/ 340 | /* some quick & dirty code to fill in the rt area and save me a lot of scratch paper */ 341 | 342 | var rt_addr = 0; 343 | var rt_straddr = 0; 344 | 345 | function addr(ptr) { 346 | //SAEF_log("autoconf.addr() %08x", ptr + SAEV_AutoConf_base); 347 | //return (uae_u32)ptr + SAEV_AutoConf_base; 348 | return ptr + SAEV_AutoConf_base; 349 | } 350 | this.db = function(data) { 351 | //SAEF_log("autoconf.db() %02x", data); 352 | rtarea_bank.baseaddr[rt_addr++] = data; 353 | } 354 | this.dw = function(data) { 355 | //SAEF_log("autoconf.dw() %04x", data); 356 | rtarea_bank.baseaddr[rt_addr++] = data >> 8; 357 | rtarea_bank.baseaddr[rt_addr++] = data & 0xff; 358 | } 359 | this.dl = function(data) { 360 | //SAEF_log("autoconf.dl() %08x", data); 361 | rtarea_bank.baseaddr[rt_addr++] = data >> 24; 362 | rtarea_bank.baseaddr[rt_addr++] = (data >> 16) & 0xff; 363 | rtarea_bank.baseaddr[rt_addr++] = (data >> 8) & 0xff; 364 | rtarea_bank.baseaddr[rt_addr++] = data & 0xff; 365 | } 366 | 367 | /*this.dbg = function(addr) { 368 | addr -= SAEV_AutoConf_base; 369 | return rtarea_bank.baseaddr[addr]; 370 | }*/ 371 | 372 | /* store strings starting at the end of the rt area and working backward. store pointer at current address */ 373 | /*uae_u32 ds_ansi (const uae_char *str) { 374 | int len; 375 | 376 | if (!str) 377 | return addr (rt_straddr); 378 | len = strlen (str) + 1; 379 | rt_straddr -= len; 380 | strcpy ((uae_char*)rtarea_bank.baseaddr + rt_straddr, str); 381 | return addr (rt_straddr); 382 | } 383 | uae_u32 ds (const TCHAR *str) { 384 | char *s = ua (str); 385 | uae_u32 v = ds_ansi (s); 386 | xfree (s); 387 | return v; 388 | } 389 | uae_u32 ds_bstr_ansi (const uae_char *str) { 390 | int len; 391 | 392 | len = strlen (str) + 2; 393 | rt_straddr -= len; 394 | while (rt_straddr & 3) 395 | rt_straddr--; 396 | rtarea_bank.baseaddr[rt_straddr] = len - 2; 397 | strcpy ((uae_char*)rtarea_bank.baseaddr + rt_straddr + 1, str); 398 | return addr (rt_straddr) >> 2; 399 | }*/ 400 | 401 | this.calltrap = function(n) { 402 | /*if (currprefs.uaeboard > 2) { 403 | this.dw(0x4eb9); // JSR rt_trampoline_ptr 404 | this.dl(rt_trampoline_ptr); 405 | uaecptr a = this.here(); 406 | this.org(rt_trampoline_ptr); 407 | this.dw(0x3f3c); // MOVE.W #n,-(SP) 408 | this.dw(n); 409 | this.dw(0x4ef9); // JMP rt_trampoline_entry 410 | this.dl(trap_entry); 411 | this.org(a); 412 | rt_trampoline_ptr += 3 * 2 + 1 * 4; 413 | } else*/ 414 | this.dw(0xA000 + n); 415 | } 416 | 417 | this.org = function(a) { 418 | if (((a & 0xffff0000) >>> 0 != 0x00f00000) && ((a & 0xffff0000) >>> 0 != SAEV_AutoConf_base)) 419 | SAEF_warn("autoconf.org() corrupt address %08X", a); 420 | rt_addr = a & 0xffff; 421 | } 422 | 423 | this.here = function() { 424 | return addr(rt_addr); 425 | } 426 | 427 | /*this.align = function(b) { 428 | rt_addr = (rt_addr + b - 1) & ~(b - 1); 429 | }*/ 430 | 431 | /*-----------------------------------------------------------------------*/ 432 | 433 | function mapped_malloc(ab) { 434 | ab.startmask = ab.start; 435 | try { 436 | //ab.baseaddr = xcalloc(uae_u8, ab.allocated + 4); 437 | ab.baseaddr = new Uint8Array(ab.allocated + 4); 438 | return true; 439 | } catch (e) { 440 | ab.baseaddr = null; 441 | return false; 442 | } 443 | } 444 | function mapped_free(ab) { 445 | //xfree(ab.baseaddr); 446 | ab.baseaddr = null; 447 | } 448 | 449 | /*static uae_u32 REGPARAM2 nullfunc (TrapContext *ctx) { 450 | write_log (_T("Null function called\n")); 451 | return 0; 452 | } 453 | static uae_u32 REGPARAM2 getchipmemsize (TrapContext *ctx) { 454 | trap_set_dreg(ctx, 1, z3chipmem_bank.allocated); 455 | trap_set_areg(ctx, 1, z3chipmem_bank.start); 456 | return chipmem_bank.allocated; 457 | } 458 | static uae_u32 REGPARAM2 uae_puts (TrapContext *ctx) { 459 | puts ((char*)get_real_address(trap_get_areg(ctx, 0))); 460 | return 0; 461 | }*/ 462 | 463 | /* OPT inline ok 464 | function rtarea_init_mem() { 465 | if (SAER.autoconf.need_uae_boot_rom()) 466 | rtarea_bank.flags &= ~SAEC_Memory_addrbank_flag_ALLOCINDIRECT; 467 | else 468 | rtarea_bank.flags |= SAEC_Memory_addrbank_flag_ALLOCINDIRECT; 469 | 470 | rtarea_bank.allocated = RTAREA_SIZE; 471 | if (!mapped_malloc(rtarea_bank)) { 472 | SAEF_fatal(SAEE_NoMemory, "autoconf.init_mem() memory exhausted"); 473 | //abort(); 474 | } 475 | }*/ 476 | this.setup = function() { //rtarea_init() 477 | rt_straddr = 0xFF00 - 2; 478 | rt_addr = 0; 479 | 480 | rt_trampoline_ptr = SAEV_AutoConf_base + RTAREA_TRAMPOLINE; 481 | trap_entry = 0; 482 | 483 | this.init_traps(); 484 | 485 | //rtarea_init_mem(); 486 | { 487 | if (this.need_uae_boot_rom()) 488 | rtarea_bank.flags &= ~SAEC_Memory_addrbank_flag_ALLOCINDIRECT; 489 | else 490 | rtarea_bank.flags |= SAEC_Memory_addrbank_flag_ALLOCINDIRECT; 491 | 492 | rtarea_bank.allocated = RTAREA_SIZE; 493 | if (!mapped_malloc(rtarea_bank)) { 494 | SAEF_fatal(SAEE_NoMemory, "autoconf.init_mem() memory exhausted"); 495 | //abort(); 496 | } 497 | } 498 | //memset(rtarea_bank.baseaddr, 0, RTAREA_SIZE); 499 | SAEF_memset(rtarea_bank.baseaddr,0, 0, RTAREA_SIZE); 500 | 501 | /*var uaever = sprintf("uae-%d.%d.%d", UAEMAJOR, UAEMINOR, UAESUBREV); 502 | var saever = sprintf("sae-%d.%d.%d", SAEC_Version, SAEC_Revision, SAEC_Patch); 503 | EXPANSION_uaeversion = ds(saever); 504 | EXPANSION_explibname = ds("expansion.library"); 505 | EXPANSION_doslibname = ds("dos.library"); 506 | EXPANSION_uaedevname = ds("uae.device");*/ 507 | 508 | this.dw(0); 509 | this.dw(0); 510 | 511 | /*#ifdef FILESYS 512 | filesys_install_code(); 513 | 514 | trap_entry = filesys_get_entry(10); 515 | write_log(_T("TRAP_ENTRY = %08x\n"), trap_entry); 516 | 517 | for (int i = 0; i < RTAREA_TRAP_DATA_SIZE / RTAREA_TRAP_DATA_SLOT_SIZE; i++) { 518 | hardware_trap_event[i] = CreateEvent(NULL, FALSE, FALSE, NULL); 519 | } 520 | #endif*/ 521 | 522 | this.define_trap(null, 0, "null"); /* Generic emulator trap */ 523 | 524 | /*var a = this.here(); 525 | // Dummy trap - removing this breaks the filesys emulation. 526 | this.org(SAEV_AutoConf_base + 0xFF00); 527 | this.calltrap(deftrap2(nullfunc, TRAPFLAG_NO_RETVAL, "")); 528 | 529 | this.org(SAEV_AutoConf_base + 0xFF80); 530 | this.calltrap(deftrapres(getchipmemsize, TRAPFLAG_DORET, "getchipmemsize")); 531 | this.dw(SAEC_AutoConf_RTS); 532 | 533 | this.org(SAEV_AutoConf_base + 0xFF10); 534 | this.calltrap(deftrapres(uae_puts, TRAPFLAG_NO_RETVAL, "uae_puts")); 535 | this.dw(SAEC_AutoConf_RTS); 536 | 537 | this.org(a);*/ 538 | 539 | SAEV_AutoConf_boot_rom_size = this.here() - SAEV_AutoConf_base; 540 | SAEF_log("autoconf.setup() boot_rom_size %d/%d", SAEV_AutoConf_boot_rom_size, RTAREA_TRAPS); 541 | if (SAEV_AutoConf_boot_rom_size >= RTAREA_TRAPS) { 542 | SAEF_fatal(SAEE_NoMemory, "autoconf.setup() RTAREA_TRAPS needs to be increased!"); 543 | //abort(); 544 | } 545 | 546 | /*#ifdef PICASSO96 547 | uaegfx_install_code(SAEV_AutoConf_base + RTAREA_RTG); 548 | #endif*/ 549 | 550 | this.org(RTAREA_TRAPS | SAEV_AutoConf_base); 551 | this.init_extended_traps(); 552 | } 553 | 554 | this.cleanup = function() { //rtarea_free() 555 | mapped_free(rtarea_bank); 556 | this.free_traps(); 557 | } 558 | 559 | this.init = function() { //rtarea_setup() 560 | var base = this.need_uae_boot_rom(); 561 | if (base) { 562 | SAEF_log("autoconf.init() RTAREA located at %08X", base); 563 | SAEV_AutoConf_base = base; 564 | } 565 | } 566 | 567 | /*-----------------------------------------------------------------------*/ 568 | 569 | this.makedatatable = function(resid, resname, type, priority, ver, rev) { 570 | var datatable = this.here(); 571 | this.dw(0xE000); /* INITBYTE */ 572 | this.dw(0x0008); /* LN_TYPE */ 573 | this.dw(type << 8); 574 | this.dw(0xE000); /* INITBYTE */ 575 | this.dw(0x0009); /* LN_PRI */ 576 | this.dw(priority << 8); 577 | this.dw(0xC000); /* INITLONG */ 578 | this.dw(0x000A); /* LN_NAME */ 579 | this.dl(resname); 580 | this.dw(0xE000); /* INITBYTE */ 581 | this.dw(0x000E); /* LIB_FLAGS */ 582 | this.dw(0x0600); /* LIBF_SUMUSED | LIBF_CHANGED */ 583 | this.dw(0xD000); /* INITWORD */ 584 | this.dw(0x0014); /* LIB_VERSION */ 585 | this.dw(ver); 586 | this.dw(0xD000); /* INITWORD */ 587 | this.dw(0x0016); /* LIB_REVISION */ 588 | this.dw(rev); 589 | this.dw(0xC000); /* INITLONG */ 590 | this.dw(0x0018); /* LIB_IDSTRING */ 591 | this.dl(resid); 592 | this.dw(0x0000); /* end of table */ 593 | return datatable; 594 | } 595 | 596 | /*-----------------------------------------------------------------------*/ 597 | /* SECT Traps */ 598 | /*-----------------------------------------------------------------------*/ 599 | 600 | const TRAPFLAG_NO_REGSAVE = 1; 601 | const TRAPFLAG_NO_RETVAL = 2; 602 | const TRAPFLAG_EXTRA_STACK = 4; 603 | const TRAPFLAG_DORET = 8; 604 | const TRAPFLAG_UAERES = 16; 605 | 606 | function Trap() { 607 | this.handler = null; /* Handler function to be invoked for this trap */ 608 | this.flags = 0; /* Trap attributes */ 609 | this.name = ""; /* For debugging purposes */ 610 | this.addr = 0; 611 | }; 612 | const MAX_TRAPS = 16; //4096; 613 | 614 | var trap_count = 1; 615 | var traps = new Array(MAX_TRAPS); 616 | for (var vi = 0; vi < MAX_TRAPS; vi++) 617 | traps[vi] = new Trap(); 618 | 619 | var hwtrap_waiting = 0; //volatile uae_atomic 620 | 621 | const trace_traps = true; 622 | 623 | /*-----------------------------------------------------------------------*/ 624 | 625 | this.find_trap = function(name) { 626 | for (var i = 0; i < trap_count; i++) { 627 | var trap = traps[i]; 628 | if ((trap.flags & TRAPFLAG_UAERES) && trap.name.length && trap.name == name) 629 | return trap.addr; 630 | } 631 | return 0; 632 | } 633 | 634 | /* 635 | * Define an emulator trap 636 | * 637 | * handler_func = host function that will be invoked to handle this trap 638 | * flags = trap attributes 639 | * name = name for debugging purposes 640 | * 641 | * returns trap number of defined trap 642 | */ 643 | this.define_trap = function(handler_func, flags, name) { 644 | if (trap_count == MAX_TRAPS) { 645 | SAEF_fatal(SAEE_Internal, "define_trap() Ran out of emulator traps. (increase MAX_TRAPS)"); 646 | //abort(); 647 | //return -1; 648 | } else { 649 | var addr = this.here(); 650 | 651 | for (var i = 0; i < trap_count; i++) { 652 | if (addr == traps[i].addr) 653 | return i; 654 | } 655 | 656 | var trap_num = trap_count++; 657 | var trap = traps[trap_num]; 658 | 659 | trap.handler = handler_func; 660 | trap.flags = flags; 661 | trap.name = name; 662 | trap.addr = addr; 663 | 664 | return trap_num; 665 | } 666 | } 667 | 668 | /* 669 | * This function is called by the 68k interpreter to handle an emulator trap. 670 | * 671 | * trap_num = number of trap to invoke 672 | * regs = current 68k state 673 | */ 674 | this.m68k_handle_trap = function(trap_num) { 675 | var trap = traps[trap_num]; 676 | var retval = 0; 677 | 678 | var has_retval = (trap.flags & TRAPFLAG_NO_RETVAL) == 0; 679 | var implicit_rts = (trap.flags & TRAPFLAG_DORET) != 0; 680 | 681 | if (trap.name.length && trace_traps) 682 | SAEF_log("m68k_handle_trap() TRAP '%s'", trap.name); 683 | 684 | if (trap_num < trap_count) { 685 | if (trap.flags & TRAPFLAG_EXTRA_STACK) { 686 | /* Handle an extended trap. 687 | * Note: the return value of this trap is passed back to 68k 688 | * space via a separate, dedicated simple trap which the trap 689 | * handler causes to be invoked when it is done. 690 | */ 691 | //trap_HandleExtendedTrap(trap.handler, has_retval); //FIX implement extended-traps 692 | SAEF_fatal(SAEE_Internal, "m68k_handle_trap() Extended-traps are not implemented."); 693 | } else { 694 | /* Handle simple trap */ 695 | //retval = (trap.handler)(null); 696 | retval = trap.handler(null); 697 | 698 | if (has_retval) { 699 | SAER_CPU_regs.d[0] = retval; 700 | SAEF_log("m68k_handle_trap() D0 = %d", retval); 701 | } 702 | if (implicit_rts) { 703 | //m68k_do_rts(); { 704 | var newpc = SAER_Memory_get32(SAER_CPU_regs.a[7]); 705 | SAER_CPU_setPC(newpc); 706 | SAER_CPU_regs.a[7] += 4; 707 | //} 708 | SAER_CPU_fill_prefetch(); 709 | } 710 | } 711 | } else 712 | SAEF_warn("m68k_handle_trap() illegal emulator trap"); 713 | } 714 | 715 | /*-----------------------------------------------------------------------*/ 716 | 717 | this.init_traps = function() { 718 | trap_count = 0; 719 | hwtrap_waiting = 0; 720 | } 721 | this.free_traps = function() { 722 | 723 | } 724 | this.init_extended_traps = function() { 725 | 726 | } 727 | } 728 | -------------------------------------------------------------------------------- /sae/copper.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | /* global references */ 20 | 21 | var SAER_Copper_cop_state = null; 22 | 23 | /*---------------------------------*/ 24 | /* global variables */ 25 | 26 | var SAEV_Copper_access = false; 27 | var SAEV_Copper_last_hpos = 0; 28 | var SAEV_Copper_enabled_thisline = 0; 29 | 30 | /*---------------------------------*/ 31 | 32 | function SAEO_Copper() { 33 | const customdelay = [ 34 | 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* 32 0x00 - 0x3e */ 35 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 - 0x5e */ 36 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 - 0x7e */ 37 | 0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0, /* 0x80 - 0x9e */ 38 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 32 0xa0 - 0xde */ 39 | /* BPLxPTH/BPLxPTL */ 40 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16 */ 41 | /* BPLCON0-3,BPLMOD1-2 */ 42 | 0,0,0,0,0,0,0,0, /* 8 */ 43 | /* BPLxDAT */ 44 | 0,0,0,0,0,0,0,0, /* 8 */ 45 | /* SPRxPTH/SPRxPTL */ 46 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 16 */ 47 | /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */ 48 | 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, 49 | /* COLORxx */ 50 | 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, 51 | /* RESERVED */ 52 | 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 53 | ]; 54 | 55 | const COP_stop = 0; 56 | const COP_waitforever = 1; 57 | const COP_read1 = 2; 58 | const COP_read2 = 3; 59 | const COP_bltwait = 4; 60 | const COP_wait_in2 = 5; 61 | const COP_skip_in2 = 6; 62 | const COP_wait1 = 7; 63 | const COP_wait = 8; 64 | const COP_skip1 = 9; 65 | const COP_strobe_delay1 = 10; 66 | const COP_strobe_delay2 = 11; 67 | const COP_strobe_delay1x = 12; 68 | const COP_strobe_delay2x = 13; 69 | const COP_strobe_extra = 14; /* just to skip current cycle when CPU wrote to COPJMP */ 70 | const COP_start_delay = 15; 71 | 72 | function copper_state() { 73 | this.i1 = 0; this.saved_i1 = 0; /* The current instruction words. */ 74 | this.i2 = 0; this.saved_i2 = 0; 75 | this.state = COP_stop; 76 | this.state_prev = COP_stop; 77 | this.ip = 0; this.saved_ip = 0; /* Instruction pointer */ 78 | this.hpos = 0; 79 | this.vpos = 0; 80 | this.ignore_next = false; 81 | this.vcmp = 0; 82 | this.hcmp = 0; 83 | this.strobe = 0; /* COPJMP1 / COPJMP2 accessed */ 84 | this.moveaddr = 0; 85 | this.movedata = 0; 86 | this.movedelay = 0; 87 | }; 88 | 89 | var cop_state = new copper_state(); 90 | SAER_Copper_cop_state = cop_state; 91 | 92 | var cop1lc = 0, cop2lc = 0, copcon = 0; 93 | 94 | //var last_copper_hpos = 0; -> SAEV_Copper_last_hpos 95 | //var copper_access = false; -> SAEV_Copper_access 96 | //var copper_enabled_thisline = 0; -> SAEV_Copper_enabled_thisline 97 | 98 | /*-----------------------------------------------------------------------*/ 99 | 100 | this.reset = function() { 101 | copcon = 0; 102 | cop_state.state = COP_stop; 103 | cop_state.movedelay = 0; 104 | cop_state.strobe = 0; 105 | cop_state.ignore_next = false; 106 | } 107 | 108 | /*-----------------------------------------------------------------------*/ 109 | 110 | this.clr_copcon = function() { //OWN used in playfield.reset_cutom() 111 | copcon = 0; 112 | } 113 | /*this.get_copxlc = function() { //OWN used in events.alloc_cycle_blitter() 114 | return cop_state.strobe == 1 ? cop1lc : cop2lc; 115 | }*/ 116 | 117 | /*this.get_copper_address = function(copno) { 118 | switch (copno) { 119 | case 1: return cop1lc; 120 | case 2: return cop2lc; 121 | case -1: return cop_state.ip; 122 | default: return 0; 123 | } 124 | }*/ 125 | 126 | this.copper_stop = function() { //called in custom.DMACON() 127 | if (SAEV_Copper_enabled_thisline) { 128 | // let MOVE to finish 129 | switch (cop_state.state) { 130 | case COP_read2: 131 | SAEV_Copper_enabled_thisline = -1; 132 | break; 133 | } 134 | } 135 | if (SAEV_Copper_enabled_thisline >= 0) { 136 | SAEV_Copper_enabled_thisline = 0; 137 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 138 | } 139 | } 140 | 141 | function check_copper_stop() { 142 | //if (SAEV_Copper_enabled_thisline < 0 && !((SAEV_Custom_dmacon & SAEC_Custom_DMAF_COPEN) && (SAEV_Custom_dmacon & SAEC_Custom_DMAF_DMAEN))) { 143 | if (SAEV_Copper_enabled_thisline < 0 && !SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN)) { 144 | SAEV_Copper_enabled_thisline = 0; 145 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 146 | } 147 | } 148 | 149 | /*-> playfield 150 | function copper_cant_read(hpos, alloc) { 151 | if (hpos + 1 >= maxhpos) // first refresh slot 152 | return 1; 153 | if ((hpos == maxhpos - 3) && (maxhpos & 1) && alloc >= 0) { 154 | //if (alloc) SAER.events.alloc_cycle(hpos, SAEC_Events_cycle_line_COPPER); 155 | return -1; 156 | } 157 | return is_bitplane_dma_inline(hpos); 158 | }*/ 159 | 160 | function put16_copper(hpos, addr, value, noget) { 161 | SAEV_Copper_access = true; 162 | //var v = custom_wput_1(hpos, addr, value, noget); 163 | var v = SAER_Custom_put16_real(hpos, addr, value, noget); 164 | SAEV_Copper_access = false; 165 | return v; 166 | } 167 | 168 | /*function dump(error, until_hpos) { 169 | SAEF_log("copper.dump() %s: vpos=%d until_hpos=%d vp=%d", error, vpos, until_hpos, vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80)); 170 | SAEF_log("copper.dump() cvcmp=%d chcmp=%d chpos=%d cvpos=%d ci1=%04X ci2=%04X", cop_state.vcmp, cop_state.hcmp, cop_state.hpos, cop_state.vpos, cop_state.saved_i1, cop_state.saved_i2); 171 | SAEF_log("copper.dump() cstate=%d ip=%x SPCFLAGS=%x iscline=%d", cop_state.state, cop_state.ip, SAEV_spcflags, SAEV_Copper_enabled_thisline); 172 | }*/ 173 | 174 | function update_copper(until_hpos) { 175 | var vp = SAER.playfield.get_vpos() & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); 176 | var c_hpos = cop_state.hpos; 177 | var maxhpos; 178 | 179 | //if (nocustom()) return; 180 | 181 | if (cop_state.state == COP_wait && vp < cop_state.vcmp) { 182 | //dump("error2", until_hpos); 183 | SAEV_Copper_enabled_thisline = 0; 184 | cop_state.state = COP_stop; 185 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 186 | return; 187 | } 188 | 189 | if (until_hpos <= SAEV_Copper_last_hpos) 190 | return; 191 | 192 | maxhpos = SAER.playfield.get_maxhpos(); 193 | if (until_hpos > (maxhpos & ~1)) 194 | until_hpos = maxhpos & ~1; 195 | 196 | for (;;) { 197 | var old_hpos = c_hpos; 198 | var hp; 199 | 200 | if (c_hpos >= until_hpos) 201 | break; 202 | 203 | 204 | /* So we know about the fetch state. */ 205 | SAER.playfield.decide_line(c_hpos); 206 | // bitplane only, don't want blitter to steal our cycles. 207 | SAER.playfield.decide_fetch(c_hpos); 208 | 209 | if (cop_state.movedelay > 0) { 210 | cop_state.movedelay--; 211 | if (cop_state.movedelay == 0) { 212 | put16_copper(c_hpos, cop_state.moveaddr, cop_state.movedata, 0); 213 | } 214 | } 215 | 216 | maxhpos = SAER.playfield.get_maxhpos(); 217 | if ((c_hpos == maxhpos - 3) && (maxhpos & 1)) 218 | c_hpos += 1; 219 | else 220 | c_hpos += 2; 221 | 222 | switch (cop_state.state) { 223 | case COP_wait_in2: { 224 | if (SAER.playfield.copper_cant_read(old_hpos, 0)) 225 | continue; 226 | cop_state.state = COP_wait1; 227 | break; 228 | } 229 | case COP_skip_in2: { 230 | if (SAER.playfield.copper_cant_read(old_hpos, 0)) 231 | continue; 232 | cop_state.state = COP_skip1; 233 | break; 234 | } 235 | case COP_strobe_extra: { 236 | // Wait 1 copper cycle doing nothing 237 | cop_state.state = COP_strobe_delay1; 238 | break; 239 | } 240 | case COP_strobe_delay1: { 241 | // First cycle after COPJMP is just like normal first read cycle 242 | // Cycle is used and needs to be free. 243 | if (SAER.playfield.copper_cant_read(old_hpos, 1)) 244 | continue; 245 | //SAER.events.alloc_cycle(old_hpos, SAEC_Events_cycle_line_COPPER); 246 | maxhpos = SAER.playfield.get_maxhpos(); 247 | if (old_hpos == maxhpos - 2) { 248 | // if COP_strobe_delay2 would cross scanlines (positioned immediately 249 | // after first strobe/refresh slot) it will disappear! 250 | cop_state.state = COP_read1; 251 | if (cop_state.strobe == 1) 252 | cop_state.ip = cop1lc; 253 | else 254 | cop_state.ip = cop2lc; 255 | cop_state.strobe = 0; 256 | } else { 257 | cop_state.state = COP_strobe_delay2; 258 | cop_state.ip += 2; 259 | } 260 | break; 261 | } 262 | case COP_strobe_delay2: { 263 | // Second cycle after COPJMP. This is the strange one. 264 | // This cycle does not need to be free 265 | // But it still gets allocated by copper if it is free = CPU and blitter can't use it. 266 | //if (!SAER.playfield.copper_cant_read(old_hpos, 0)) SAER.events.alloc_cycle(old_hpos, SAEC_Events_cycle_line_COPPER); 267 | 268 | cop_state.state = COP_read1; 269 | // Next cycle finally reads from new pointer 270 | if (cop_state.strobe == 1) 271 | cop_state.ip = cop1lc; 272 | else 273 | cop_state.ip = cop2lc; 274 | cop_state.strobe = 0; 275 | break; 276 | } 277 | case COP_strobe_delay1x: { 278 | // First cycle after COPJMP and Copper was waiting. This is the buggy one. 279 | // Cycle can be free and copper won"t allocate it. 280 | // If Blitter uses this cycle = Copper"s PC gets copied to blitter DMA pointer.. 281 | cop_state.state = COP_strobe_delay2x; 282 | break; 283 | } 284 | case COP_strobe_delay2x: { 285 | // Second cycle fetches following word and tosses it away. Must be free cycle 286 | // but it is not allocated, blitter or cpu can still use it. 287 | if (SAER.playfield.copper_cant_read(old_hpos, 1)) 288 | continue; 289 | //SAER_Events_cycle_line[old_hpos] |= SAEC_Events_cycle_line_COPPER_SPECIAL; 290 | cop_state.state = COP_read1; 291 | // Next cycle finally reads from new pointer 292 | if (cop_state.strobe == 1) 293 | cop_state.ip = cop1lc; 294 | else 295 | cop_state.ip = cop2lc; 296 | cop_state.strobe = 0; 297 | break; 298 | } 299 | case COP_start_delay: { 300 | // cycle after vblank strobe fetches word from old pointer first 301 | if (SAER.playfield.copper_cant_read(old_hpos, 1)) 302 | continue; 303 | cop_state.state = COP_read1; 304 | //cop_state.i1 = SAEV_Custom_last_value = SAER_Memory_chipGet16_indirect(cop_state.ip); 305 | cop_state.i1 = SAEV_Custom_last_value = (SAER_Memory_chipData[cop_state.ip] << 8) | SAER_Memory_chipData[cop_state.ip + 1]; 306 | //SAER.events.alloc_cycle(old_hpos, SAEC_Events_cycle_line_COPPER); 307 | cop_state.ip = cop1lc; 308 | break; 309 | } 310 | case COP_read1: { 311 | if (SAER.playfield.copper_cant_read(old_hpos, 1)) 312 | continue; 313 | //cop_state.i1 = SAEV_Custom_last_value = SAER_Memory_chipGet16_indirect(cop_state.ip); 314 | cop_state.i1 = SAEV_Custom_last_value = (SAER_Memory_chipData[cop_state.ip] << 8) | SAER_Memory_chipData[cop_state.ip + 1]; 315 | //SAER.events.alloc_cycle(old_hpos, SAEC_Events_cycle_line_COPPER); 316 | cop_state.ip += 2; 317 | cop_state.state = COP_read2; 318 | break; 319 | } 320 | case COP_read2: { 321 | if (SAER.playfield.copper_cant_read(old_hpos, 1)) 322 | continue; 323 | //cop_state.i2 = SAEV_Custom_last_value = SAER_Memory_chipGet16_indirect(cop_state.ip); 324 | cop_state.i2 = SAEV_Custom_last_value = (SAER_Memory_chipData[cop_state.ip] << 8) | SAER_Memory_chipData[cop_state.ip + 1]; 325 | //SAER.events.alloc_cycle(old_hpos, SAEC_Events_cycle_line_COPPER); 326 | cop_state.ip += 2; 327 | cop_state.saved_i1 = cop_state.i1; 328 | cop_state.saved_i2 = cop_state.i2; 329 | cop_state.saved_ip = cop_state.ip; 330 | 331 | if (cop_state.i1 & 1) { // WAIT or SKIP 332 | cop_state.ignore_next = false; 333 | if (cop_state.i2 & 1) 334 | cop_state.state = COP_skip_in2; 335 | else 336 | cop_state.state = COP_wait_in2; 337 | } else { // MOVE 338 | var reg = cop_state.i1 & 0x1FE; 339 | var data = cop_state.i2; 340 | cop_state.state = COP_read1; 341 | test_copper_dangerous(reg); 342 | if (!SAEV_Copper_enabled_thisline) { 343 | //goto out; //was "dangerous" register -> copper stopped 344 | cop_state.hpos = c_hpos; 345 | SAEV_Copper_last_hpos = until_hpos; 346 | return; 347 | } 348 | if (cop_state.ignore_next) 349 | reg = 0x1fe; 350 | 351 | if (reg == 0x88) { 352 | cop_state.strobe = 1; 353 | cop_state.state = COP_strobe_delay1; 354 | } else if (reg == 0x8a) { 355 | cop_state.strobe = 2; 356 | cop_state.state = COP_strobe_delay1; 357 | } else { 358 | if (customdelay[reg >> 1]) { 359 | cop_state.moveaddr = reg; 360 | cop_state.movedata = data; 361 | cop_state.movedelay = customdelay[reg >> 1]; 362 | } else 363 | put16_copper(old_hpos, reg, data, 0); 364 | } 365 | cop_state.ignore_next = false; 366 | } 367 | check_copper_stop(); 368 | break; 369 | } 370 | case COP_wait1: { 371 | cop_state.state = COP_wait; 372 | 373 | cop_state.vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8; 374 | cop_state.hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xFE); 375 | 376 | vp = SAER.playfield.get_vpos() & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); 377 | 378 | if (cop_state.saved_i1 == 0xFFFF && cop_state.saved_i2 == 0xFFFE) { 379 | cop_state.state = COP_waitforever; 380 | SAEV_Copper_enabled_thisline = 0; 381 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 382 | //goto out; 383 | cop_state.hpos = c_hpos; 384 | SAEV_Copper_last_hpos = until_hpos; 385 | return; 386 | } 387 | if (vp < cop_state.vcmp) { 388 | SAEV_Copper_enabled_thisline = 0; 389 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 390 | //goto out; 391 | cop_state.hpos = c_hpos; 392 | SAEV_Copper_last_hpos = until_hpos; 393 | return; 394 | } 395 | /* fall through */ 396 | } 397 | case COP_wait: { 398 | var ch_comp = c_hpos; 399 | if (ch_comp & 1) 400 | ch_comp = 0; 401 | 402 | /* First handle possible blitter wait 403 | * Must be before following free cycle check */ 404 | if ((cop_state.saved_i2 & 0x8000) == 0) { 405 | SAER.blitter.decide_blitter(old_hpos); 406 | if (SAEV_Blitter_bltstate != SAEC_Blitter_bltstate_DONE) { 407 | /* We need to wait for the blitter. */ 408 | cop_state.state = COP_bltwait; 409 | SAEV_Copper_enabled_thisline = 0; 410 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 411 | //goto out; 412 | cop_state.hpos = c_hpos; 413 | SAEV_Copper_last_hpos = until_hpos; 414 | return; 415 | } 416 | } 417 | 418 | if (SAER.playfield.copper_cant_read(old_hpos, 0)) 419 | continue; 420 | 421 | hp = ch_comp & (cop_state.saved_i2 & 0xFE); 422 | if (vp == cop_state.vcmp && hp < cop_state.hcmp) 423 | break; 424 | 425 | cop_state.state = COP_read1; 426 | break; 427 | } 428 | case COP_skip1: { 429 | var vcmp, hcmp, vp1, hp1; 430 | 431 | maxhpos = SAER.playfield.get_maxhpos(); 432 | if (c_hpos >= (maxhpos & ~1) || (c_hpos & 1)) 433 | break; 434 | 435 | if (SAER.playfield.copper_cant_read(old_hpos, 0)) 436 | continue; 437 | 438 | vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8; 439 | hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xFE); 440 | vp1 = SAER.playfield.get_vpos() & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); 441 | hp1 = c_hpos & (cop_state.saved_i2 & 0xFE); 442 | 443 | if ((vp1 > vcmp || (vp1 == vcmp && hp1 >= hcmp)) && ((cop_state.saved_i2 & 0x8000) != 0 || SAEV_Blitter_bltstate == SAEC_Blitter_bltstate_DONE)) 444 | cop_state.ignore_next = true; 445 | 446 | cop_state.state = COP_read1; 447 | break; 448 | } 449 | default: 450 | break; 451 | } 452 | } 453 | 454 | //out: 455 | cop_state.hpos = c_hpos; 456 | SAEV_Copper_last_hpos = until_hpos; 457 | } 458 | 459 | this.compute_spcflag_copper = function(hpos) { 460 | var wasenabled = SAEV_Copper_enabled_thisline; 461 | 462 | SAEV_Copper_enabled_thisline = 0; 463 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 464 | if (!SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN) || cop_state.state == COP_stop || cop_state.state == COP_waitforever || cop_state.state == COP_bltwait) //|| nocustom()) 465 | return; 466 | 467 | if (cop_state.state == COP_wait) { 468 | var vp = SAER.playfield.get_vpos() & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); 469 | if (vp < cop_state.vcmp) 470 | return; 471 | } 472 | // do not use past cycles if starting for the first time in this line 473 | // (write to DMACON for example) hpos+1 for long lines 474 | if (!wasenabled && cop_state.hpos < hpos && hpos < SAER.playfield.get_maxhpos()) { 475 | var maxhpos_short = SAER.playfield.get_maxhpos_short(); 476 | hpos = (hpos + 2) & ~1; 477 | if (hpos > (maxhpos_short & ~1)) 478 | hpos = maxhpos_short & ~1; 479 | cop_state.hpos = hpos; 480 | } 481 | // if COPJMPx was written while DMA was disabled, advance to next state, 482 | // COP_strobe_extra is single cycle only and does not need free bus. 483 | // (copper state emulation does not run if DMA is disabled) 484 | if (!wasenabled && cop_state.state == COP_strobe_extra) 485 | cop_state.state = COP_strobe_delay1; 486 | 487 | SAEV_Copper_enabled_thisline = 1; 488 | SAEF_setSpcFlags(SAEC_spcflag_COPPER); 489 | } 490 | 491 | this.blitter_done_notify = function(hpos) { 492 | if (cop_state.state != COP_bltwait) 493 | return; 494 | 495 | var vpos = SAER.playfield.get_vpos(); 496 | var vp_wait = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); 497 | var vp = vpos; 498 | var maxhpos = SAER.playfield.get_maxhpos(); 499 | 500 | hpos++; 501 | hpos &= ~1; 502 | if (hpos >= maxhpos) { 503 | hpos -= maxhpos; 504 | vp++; 505 | } 506 | cop_state.hpos = hpos; 507 | cop_state.vpos = vp; 508 | cop_state.state = COP_wait; 509 | /* No need to check blitter state again */ 510 | cop_state.saved_i2 |= 0x8000; 511 | 512 | if (SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN) && vp_wait >= cop_state.vcmp) { 513 | SAEV_Copper_enabled_thisline = 1; 514 | SAEF_setSpcFlags(SAEC_spcflag_COPPER); 515 | } else 516 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 517 | } 518 | 519 | this.cycle = function() { //do_copper() 520 | var hpos = SAER.events.current_hpos(); 521 | update_copper(hpos); 522 | } 523 | 524 | this.sync_copper_with_cpu = function(hpos, do_schedule) { 525 | /* Need to let the copper advance to the current position. */ 526 | if (SAEV_Copper_enabled_thisline) 527 | update_copper(hpos); 528 | } 529 | 530 | /*-----------------------------------------------------------------------*/ 531 | 532 | function test_copper_dangerous(address) { 533 | var addr = address & 0x01fe; 534 | if (addr < ((copcon & 2) ? ((SAEV_config.chipset.mask & SAEC_Config_Chipset_Mask_ECS_AGNUS) ? 0 : 0x40) : 0x80)) { 535 | cop_state.state = COP_stop; 536 | SAEV_Copper_enabled_thisline = 0; 537 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 538 | return true; 539 | } 540 | return false; 541 | } 542 | 543 | /*function immediate_copper(num) { 544 | var pos = 0; 545 | var oldpos = 0; 546 | 547 | cop_state.state = COP_stop; 548 | cop_state.vpos = SAER.playfield.get_vpos(); 549 | cop_state.hpos = SAER.events.current_hpos() & ~1; 550 | cop_state.ip = num == 1 ? cop1lc : cop2lc; 551 | 552 | while (pos < (maxvpos << 5)) { 553 | if (oldpos > pos) 554 | pos = oldpos; 555 | if (!SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN)) 556 | break; 557 | if (cop_state.ip >= SAEV_config.memory.chipSize && cop_state.ip < currprefs.z3chipmem_start && cop_state.ip >= currprefs.z3chipmem_start + currprefs.z3chipmem_size) 558 | break; 559 | pos++; 560 | oldpos = pos; 561 | //cop_state.i1 = SAER_Memory_chipGet16_indirect(cop_state.ip); 562 | //cop_state.i2 = SAER_Memory_chipGet16_indirect(cop_state.ip + 2); 563 | cop_state.i1 = (SAER_Memory_chipData[cop_state.ip ] << 8) | SAER_Memory_chipData[cop_state.ip + 1]; 564 | cop_state.i2 = (SAER_Memory_chipData[cop_state.ip + 2] << 8) | SAER_Memory_chipData[cop_state.ip + 3]; 565 | cop_state.ip += 4; 566 | if (!(cop_state.i1 & 1)) { // move 567 | cop_state.i1 &= 0x1fe; 568 | if (cop_state.i1 == 0x88) { 569 | cop_state.ip = cop1lc; 570 | continue; 571 | } 572 | if (cop_state.i1 == 0x8a) { 573 | cop_state.ip = cop2lc; 574 | continue; 575 | } 576 | if (test_copper_dangerous(cop_state.i1)) 577 | break; 578 | 579 | //custom_wput_1(0, cop_state.i1, cop_state.i2, 0); 580 | SAER_Custom_put16_real(0, addr, value, 0); 581 | } else { // wait or skip 582 | if ((cop_state.i1 >> 8) > ((pos >> 5) & 0xff)) 583 | pos = (((pos >> 5) & 0x100) | ((cop_state.i1 >> 8)) << 5) | ((cop_state.i1 & 0xff) >> 3); 584 | if (cop_state.i1 >= 0xffdf && cop_state.i2 == 0xfffe) 585 | break; 586 | } 587 | } 588 | cop_state.state = COP_stop; 589 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 590 | }*/ 591 | 592 | this.COP1LCH = function(v) { 593 | v = v & (SAEV_config.chipset.mask == SAEC_Config_Chipset_Mask_OCS ? 7 : 31); //OWN 594 | cop1lc = ((cop1lc & 0x0000ffff) | (v << 16)) >>> 0; 595 | } 596 | this.COP1LCL = function(v) { 597 | cop1lc = ((cop1lc & 0xffff0000) | (v & 0xfffe)) >>> 0; 598 | } 599 | this.COP2LCH = function(v) { 600 | v = v & (SAEV_config.chipset.mask == SAEC_Config_Chipset_Mask_OCS ? 7 : 31); //OWN 601 | cop2lc = ((cop2lc & 0x0000ffff) | (v << 16)) >>> 0; 602 | } 603 | this.COP2LCL = function(v) { 604 | cop2lc = ((cop2lc & 0xffff0000) | (v & 0xfffe)) >>> 0; 605 | } 606 | 607 | // vblank = copper starts at hpos=2 608 | // normal COPJMP write: takes 2 more cycles 609 | this.COPJMP = function(num, vblank) { 610 | var oldstrobe = cop_state.strobe; 611 | var wasstopped = cop_state.state == COP_stop && !vblank; 612 | 613 | /*if (SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN) && (cop_state.saved_i1 != 0xffff || cop_state.saved_i2 != 0xfffe)) 614 | SAEF_warn("copper.COPJMP vblank without copper ending %08x (%08x %08x)", cop_state.ip, cop1lc, cop2lc);*/ 615 | 616 | SAEF_clrSpcFlags(SAEC_spcflag_COPPER); 617 | cop_state.ignore_next = false; 618 | 619 | if (!oldstrobe) 620 | cop_state.state_prev = cop_state.state; 621 | if ((cop_state.state == COP_wait || cop_state.state == COP_waitforever) && !vblank && SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN)) { 622 | cop_state.state = COP_strobe_delay1x; 623 | } else { 624 | cop_state.state = vblank ? COP_start_delay : (SAEV_Copper_access ? COP_strobe_delay1 : COP_strobe_extra); 625 | } 626 | cop_state.vpos = SAER.playfield.get_vpos(); 627 | cop_state.hpos = SAER.events.current_hpos() & ~1; 628 | SAEV_Copper_enabled_thisline = 0; 629 | cop_state.strobe = num; 630 | 631 | /*if (nocustom()) { 632 | immediate_copper(num); 633 | return; 634 | }*/ 635 | if (SAEF_Custom_dmaen(SAEC_Custom_DMAF_COPEN)) { 636 | this.compute_spcflag_copper(SAER.events.current_hpos()); 637 | } else if (wasstopped || (oldstrobe > 0 && oldstrobe != num && cop_state.state_prev == COP_wait)) { 638 | /* dma disabled, copper idle and accessed both COPxJMPs -> copper stops! */ 639 | cop_state.state = COP_stop; 640 | } 641 | } 642 | 643 | this.COPCON = function(a) { 644 | copcon = a; 645 | } 646 | } 647 | -------------------------------------------------------------------------------- /sae/disassembler.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | -------------------------------------------------------------------------*/ 17 | /* errors (copied from amiga.js) */ 18 | 19 | function SAEO_Error(err, msg) { 20 | this.err = err; 21 | this.msg = msg; 22 | } 23 | SAEO_Error.prototype = new Error; 24 | 25 | const SAEE_None = 0; 26 | 27 | const SAEE_AlreadyRunning = 1; 28 | const SAEE_NotRunning = 2; 29 | const SAEE_NoTimer = 3; 30 | const SAEE_NoMemory = 4; 31 | const SAEE_Assert = 5; 32 | const SAEE_Internal = 6; 33 | 34 | const SAEE_Config_Invalid = 10; 35 | 36 | const SAEE_CPU_Internal = 20; 37 | const SAEE_CPU_Requires68020 = 21; 38 | const SAEE_CPU_Requires680EC20 = 22; 39 | const SAEE_CPU_Requires68030 = 23; 40 | const SAEE_CPU_Requires68040 = 24; 41 | 42 | /*-----------------------------------------------------------------------*/ 43 | 44 | function ScriptedDisAssembler() { 45 | this.cpu = new SAEO_CPU(); 46 | var err = this.cpu.setup_da(68030); 47 | if (err != SAEE_None) 48 | throw err; 49 | 50 | /*---------------------------------*/ 51 | 52 | this.getConfig = function() { 53 | return this.cpu.getConfig_da(); 54 | } 55 | this.disassemble = function() { 56 | return this.cpu.disassemble(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sae/dongle.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | /* 20 | RoboCop 3 21 | - set firebutton as output 22 | - read JOY1DAT 23 | - pulse firebutton (high->low) 24 | - read JOY1DAT 25 | - JOY1DAT bit 8 must toggle 26 | 27 | Leader Board 28 | - JOY1DAT, both up and down active (0x0101) 29 | 30 | B.A.T. II 31 | - set all serial pins as output except CTS 32 | - game pulses DTR (high->low) 33 | - CTS must be one 34 | - delay 35 | - CTS must be zero 36 | 37 | Italy '90 Soccer 38 | - 220k resistor between pins 5 (+5v) and 7 (POTX) 39 | - POT1DAT POTX must be between 0x32 and 0x60 40 | 41 | Dames Grand Maitre 42 | - read POT1 43 | - POT1X != POT1Y 44 | - POT1Y * 256 / POT1X must be between 450 and 500 45 | 46 | Rugby Coach 47 | - JOY1DAT, left, up and down active (0x0301) 48 | 49 | Cricket Captain 50 | - JOY0DAT bits 0 and 1: 51 | - 10 01 11 allowed 52 | - must continuously change state 53 | 54 | Leviathan 55 | - same as Leaderboard but in mouse port 56 | 57 | Logistix/SuperBase 58 | - second button must be high 59 | - POT1X = 150k 60 | - POT1Y = 100k 61 | - POT1X * 10 / POT1Y must be between 12 and 33 62 | */ 63 | 64 | function SAEO_Dongle() { 65 | const NONE = 0; 66 | const ROBOCOP3 = 1; //Joy2 67 | const LEADERBOARD = 2; //Joy2 68 | const BAT2 = 3; //Ser 69 | const ITALY90 = 4; //Joy2 70 | const DAMESGRANDMAITRE = 5; //Joy2 71 | const RUGBYCOACH = 6; //Joy2 72 | const CRICKETCAPTAIN = 7; //Joy1 73 | const LEVIATHAN = 8; //Joy1 74 | const LOGISTIX = 9; //Joy2 75 | 76 | const CYCLE_UNIT_200 = SAEC_Events_CYCLE_UNIT * 200; 77 | 78 | var flag = 0; //int 79 | var cycles = 0; //unsigned int 80 | 81 | /*var oldcia = new Array(2); //u8 [2][16] 82 | oldcia[0] = new Uint8Array(16); 83 | oldcia[1] = new Uint8Array(16);*/ 84 | 85 | /*---------------------------------*/ 86 | 87 | this.cia_read = function(cia, reg, val) { //dongle_cia_read() 88 | switch (SAEV_config.dongle) { 89 | case BAT2: { 90 | if (cia == 1 && reg == 0) { 91 | if (flag == 0 || SAEV_Events_currcycle > cycles + CYCLE_UNIT_200) { 92 | val &= ~0x10; 93 | flag = 0; 94 | } else 95 | val |= 0x10; 96 | } 97 | break; 98 | } 99 | } 100 | return val; 101 | } 102 | 103 | this.cia_write = function(cia, reg, val) { //dongle_cia_write() 104 | switch (SAEV_config.dongle) { 105 | case NONE: 106 | return; 107 | case ROBOCOP3: { 108 | if (cia == 0 && reg == 0 && (val & 0x80)) 109 | flag ^= 1; 110 | break; 111 | } 112 | case BAT2: { 113 | if (cia == 1 && reg == 0 && !(val & 0x80)) { 114 | flag = 1; 115 | cycles = SAEV_Events_currcycle; 116 | } 117 | break; 118 | } 119 | } 120 | //oldcia[cia][reg] = val; 121 | } 122 | 123 | /*---------------------------------*/ 124 | 125 | this.joytest = function(val) {} //dongle_joytest() 126 | 127 | this.joydat = function(port, val) { //dongle_joydat() 128 | switch (SAEV_config.dongle) { 129 | case NONE: 130 | break; 131 | case ROBOCOP3: { 132 | if (port == 1 && flag != 0) 133 | val += 0x100; 134 | break; 135 | } 136 | case LEADERBOARD: { 137 | if (port == 1) { 138 | val &= ~0x0303; 139 | val |= 0x0101; 140 | } 141 | break; 142 | } 143 | case LEVIATHAN: { 144 | if (port == 0) { 145 | val &= ~0x0303; 146 | val |= 0x0101; 147 | } 148 | break; 149 | } 150 | case RUGBYCOACH: { 151 | if (port == 1) { 152 | val &= ~0x0303; 153 | val|= 0x0301; 154 | } 155 | break; 156 | } 157 | case CRICKETCAPTAIN: { 158 | if (port == 0) { 159 | val &= ~0x0003; 160 | if (flag == 0) 161 | val |= 0x0001; 162 | else 163 | val |= 0x0002; 164 | } 165 | flag ^= 1; 166 | break; 167 | } 168 | } 169 | return val; 170 | } 171 | 172 | /*---------------------------------*/ 173 | 174 | this.potgo = function(val) { //dongle_potgo() 175 | switch (SAEV_config.dongle) { 176 | case NONE: 177 | return; 178 | case ITALY90: 179 | case LOGISTIX: 180 | case DAMESGRANDMAITRE: 181 | //flag = (uaerand() & 7) - 3; 182 | flag = ((Math.random() * 8) >>> 0) - 3; 183 | break; 184 | } 185 | } 186 | 187 | this.potgor = function(val) { //dongle_potgor() 188 | switch (SAEV_config.dongle) { 189 | case LOGISTIX: 190 | val |= 1 << 14; 191 | break; 192 | } 193 | return val; 194 | } 195 | 196 | /*---------------------------------*/ 197 | 198 | this.analogjoy = function(joy, axis) { //dongle_analogjoy() 199 | var v = -1; //int 200 | 201 | switch (SAEV_config.dongle) { 202 | case NONE: 203 | return -1; 204 | case ITALY90: 205 | if (joy == 1 && axis == 0) 206 | v = 73; 207 | break; 208 | case LOGISTIX: 209 | if (joy == 1) { 210 | if (axis == 0) 211 | v = 21; 212 | if (axis == 1) 213 | v = 10; 214 | } 215 | break; 216 | case DAMESGRANDMAITRE: 217 | if (joy == 1) { 218 | if (axis == 1) 219 | v = 80; 220 | if (axis == 0) 221 | v = 43; 222 | } 223 | break; 224 | } 225 | if (v >= 0) { 226 | v += flag; 227 | if (v < 0) 228 | v = 0; 229 | } 230 | return v; 231 | } 232 | 233 | /*---------------------------------*/ 234 | 235 | this.reset = function() { //dongle_reset() 236 | flag = 0; 237 | cycles = 0; //OWN 238 | 239 | //memset (oldcia, 0, sizeof oldcia); 240 | //for (var i = 0; i < 16; i++) oldcia[0][i] = oldcia[1][i] = 0; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /sae/events.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | /* global constants */ 20 | 21 | const SAEC_Events_CYCLE_UNIT = 512; 22 | const SAEC_Events_CYCLE_UNIT_INV = 1.0 / SAEC_Events_CYCLE_UNIT; /* mul is always faster than div */ 23 | const SAEC_Events_CYCLE_MAX = 0x100000000 * SAEC_Events_CYCLE_UNIT; 24 | 25 | /*---------------------------------*/ 26 | 27 | const SAEC_Events_EV_CIA = 0; 28 | const SAEC_Events_EV_AUDIO = 1; 29 | const SAEC_Events_EV_HSYNC = 2; 30 | 31 | const SAEC_Events_EV2_BLITTER = 0; 32 | const SAEC_Events_EV2_DISK = 1; 33 | 34 | const SAEC_Events_syncbase = 1000000; 35 | 36 | /*---------------------------------*/ 37 | 38 | /*const SAEC_Events_cycle_line_REFRESH = 1; 39 | const SAEC_Events_cycle_line_STROBE = 2; 40 | const SAEC_Events_cycle_line_MISC = 3; 41 | const SAEC_Events_cycle_line_SPRITE = 4; 42 | const SAEC_Events_cycle_line_COPPER = 5; 43 | const SAEC_Events_cycle_line_BLITTER = 6; 44 | const SAEC_Events_cycle_line_CPU = 7; 45 | //const SAEC_Events_cycle_line_CPUNASTY = 8; 46 | const SAEC_Events_cycle_line_COPPER_SPECIAL = 0x10; 47 | const SAEC_Events_cycle_line_MASK = 0x0f;*/ 48 | 49 | /*---------------------------------*/ 50 | /* global references */ 51 | 52 | var SAER_Events_eventtab = null; 53 | 54 | //var SAER_Events_cycle_line = null; 55 | 56 | /*---------------------------------*/ 57 | /* global variables */ 58 | 59 | var SAEV_Events_currcycle = 0; 60 | var SAEV_Events_bogusframe = 0; 61 | var SAEV_Events_timeframes = 0; 62 | var SAEV_Events_hsync_counter = 0; 63 | var SAEV_Events_vsync_counter = 0; 64 | var SAEV_Events_frameskiptime = 0; 65 | var SAEV_Events_reflowtime = 0; //OWN 66 | 67 | /*---------------------------------*/ 68 | 69 | function SAEO_Events() { 70 | const EV_MISC = 3; 71 | const EV_MAX = 4; 72 | function Event() { 73 | this.active = false; 74 | this.evtime = 0; 75 | this.oldcycles = 0; 76 | this.handler = null; 77 | }; 78 | var eventtab = new Array(EV_MAX); for (i = 0; i < EV_MAX; i++) eventtab[i] = new Event(); 79 | SAER_Events_eventtab = eventtab; 80 | 81 | const EV2_MISC = 2; 82 | const EV2_MAX = 12; 83 | function Event2() { 84 | this.active = false; 85 | this.evtime = 0; 86 | this.data = null; 87 | this.handler = null; 88 | }; 89 | var eventtab2 = new Array(EV2_MAX); for (i = 0; i < EV2_MAX; i++) eventtab2[i] = new Event2(); 90 | 91 | //var currcycle = 0; -> SAEV_Events_currcycle 92 | var nextevent = 0; 93 | var is_syncline = 0, is_syncline_end = 0; 94 | //var vblank_found_chipset = false; 95 | //var sleeps_remaining = 0; 96 | var linecounter = 0; 97 | 98 | //var syncbase = 0; -> SAEC_Events_syncbase 99 | var vsyncmintime = 0, vsyncmaxtime = 0, vsyncwaittime = 0; 100 | var vsynctimebase = 0; 101 | //var rpt_did_reset = 0; 102 | 103 | //var frameskiptime = 0; -> SAEV_Events_frameskiptime 104 | var vsynctimeperline = 0; //global 105 | 106 | var dmal = 0, dmal_hpos = 0; //u16 107 | 108 | //in framewait() 109 | const MAVG_VSYNC_SIZE = 128; 110 | var ma_frameskipt = new SAEO_MAvg(MAVG_VSYNC_SIZE); 111 | //var ma_adjust = new mavg_data(); 112 | //var ma_legacy = new mavg_data(); 113 | //var ma_skip = new mavg_data(); 114 | var ma_reflowt = new SAEO_MAvg(10); //OWN 115 | //var vsync_time = 0; 116 | 117 | //in MISC_handler() 118 | var dorecheck = false; 119 | var recursive = 0; 120 | 121 | /* Statistics */ 122 | const FPSCOUNTER_MAVG_SIZE = 10 123 | var fps_mavg = new SAEO_MAvg(FPSCOUNTER_MAVG_SIZE); 124 | var idle_mavg = new SAEO_MAvg(FPSCOUNTER_MAVG_SIZE); 125 | 126 | //var bogusframe = 0; //-> SAEV_Events_bogusframe 127 | //var timeframes = 0; //-> SAEV_Events_timeframes 128 | var frametime = 0, lastframetime = 0; //global 129 | var idletime = 0; //global 130 | //var hsync_counter = 0; -> SAEV_Events_hsync_counter 131 | //var vsync_counter = 0; -> SAEV_Events_vsync_counter 132 | 133 | //event2_newevent_xx() 134 | var nextno = EV2_MISC; 135 | 136 | const PISSOFF_NOJIT_VALUE = 256 * SAEC_Events_CYCLE_UNIT; 137 | var pissoff = 0; 138 | //#define countdown pissoff 139 | 140 | //#ifdef CPUEMU_13 141 | //var cycle_line = new Uint8Array(256 + 1); 142 | //SAER_Events_cycle_line = cycle_line; 143 | //#endif 144 | 145 | /*-----------------------------------------------------------------------*/ 146 | 147 | this.reset = function() { //init_eventtab() 148 | if (eventtab[SAEC_Events_EV_HSYNC].handler === null) { //OWN 149 | eventtab[SAEC_Events_EV_CIA].handler = function() { SAER.cia.handler(); }; 150 | eventtab[SAEC_Events_EV_HSYNC].handler = function() { SAER.playfield.hsync_handler(); }; 151 | eventtab[SAEC_Events_EV_AUDIO].handler = function() { SAER.audio.handler(); }; 152 | eventtab[EV_MISC].handler = MISC_handler; 153 | 154 | eventtab2[SAEC_Events_EV2_BLITTER].handler = function(data) { SAER.blitter.handler(data); }; 155 | eventtab2[SAEC_Events_EV2_DISK].handler = function(data) { SAER.disk.handler(data); }; 156 | } 157 | 158 | nextevent = 0; 159 | nextno = EV2_MISC; //OWN 160 | for (var i = 0; i < EV_MAX; i++) { 161 | eventtab[i].active = false; 162 | eventtab[i].oldcycles = SAEV_Events_currcycle; 163 | } 164 | eventtab[SAEC_Events_EV_HSYNC].evtime = SAEV_Events_currcycle + SAER.playfield.get_maxhpos() * SAEC_Events_CYCLE_UNIT; 165 | eventtab[SAEC_Events_EV_HSYNC].active = true; 166 | 167 | for (i = 0; i < EV2_MAX; i++) 168 | eventtab2[i].active = false; 169 | 170 | this.schedule(); 171 | 172 | //OWN 173 | dorecheck = false; 174 | recursive = 0; 175 | //nextno = EV2_MISC; 176 | fpscounter_reset(); 177 | ma_frameskipt.clr(); 178 | ma_reflowt.clr(); 179 | //reset_frame_rate_hack(); 180 | //sleeps_remaining = 0; 181 | linecounter = 0; 182 | } 183 | 184 | this.clr_dmal = function() { 185 | dmal = 0; 186 | } 187 | 188 | this.pauseResume = function(pause) { 189 | if (pause) { 190 | SAER.gui.data.fps = 0; 191 | SAER.gui.data.idle = 0; 192 | SAER.gui.fps(0, 0, 1); 193 | } else { 194 | dmal = 0; 195 | fpscounter_reset(); 196 | ma_frameskipt.clr(); 197 | } 198 | } 199 | 200 | /*-----------------------------------------------------------------------*/ 201 | 202 | this.reset_frame_rate_hack = function() { 203 | if (SAEV_config.cpu.speed < 0) { 204 | //rpt_did_reset = 1; 205 | is_syncline = 0; 206 | vsyncmintime = SAEF_now() + vsynctimebase; 207 | //SAEF_log("events.reset_frame_rate_hack() %d", vsyncmintime); 208 | } 209 | } 210 | this.calc_vsynctimebase = function(hz) { 211 | vsynctimebase = SAEC_Events_syncbase / hz >>> 0; 212 | SAEF_log("events.calc_vsynctimebase() %d us (%f)", vsynctimebase, hz); 213 | this.reset_frame_rate_hack(); 214 | return vsynctimebase; 215 | } 216 | 217 | this.schedule = function() { 218 | var mintime = SAEC_Events_CYCLE_MAX; 219 | for (var i = 0; i < EV_MAX; i++) { 220 | if (eventtab[i].active) { 221 | var eventtime = eventtab[i].evtime - SAEV_Events_currcycle; 222 | if (eventtime < mintime) 223 | mintime = eventtime; 224 | } 225 | } 226 | nextevent = SAEV_Events_currcycle + mintime; 227 | } 228 | 229 | /*this.get_cycles = function() { //OPT inline ok 230 | return SAEV_Events_currcycle; 231 | } 232 | this.set_cycles = function(x) { 233 | SAEV_Events_currcycle = x; 234 | eventtab[SAEC_Events_EV_HSYNC].oldcycles = x; 235 | } 236 | this.cycles_in_range = function(endcycles) { //OPT inline ok, used in disk.DSKBYTR() 237 | return endcycles - SAEV_Events_currcycle > 0; 238 | }*/ 239 | 240 | /*function current_hpos_safe() { //OPT inline ok 241 | return ((SAEV_Events_currcycle - eventtab[SAEC_Events_EV_HSYNC].oldcycles) * SAEC_Events_CYCLE_UNIT_INV) >>> 0; 242 | }*/ 243 | this.current_hpos = function() { 244 | //var hp = current_hpos_safe(); 245 | var hp = ((SAEV_Events_currcycle - eventtab[SAEC_Events_EV_HSYNC].oldcycles) * SAEC_Events_CYCLE_UNIT_INV) >>> 0; 246 | if (hp < 0 || hp > 256) { 247 | SAEF_error("events.current_hpos() hpos = %d !?", hp); 248 | hp = 0; 249 | } 250 | return hp; 251 | } 252 | 253 | /*-----------------------------------------------------------------------*/ 254 | 255 | this.do_cycles = function(cycles_to_add) { //do_cycles_slow() 256 | if ((pissoff -= cycles_to_add) >= 0) 257 | return; 258 | 259 | cycles_to_add = -pissoff; 260 | pissoff = 0; 261 | 262 | //if (cycles_to_add == 0) SAEF_warn("event.do_cycles() cycles_to_add == 0"); 263 | 264 | while ((nextevent - SAEV_Events_currcycle) <= cycles_to_add) { 265 | // Keep only CPU emulation running while waiting for sync point. 266 | if (is_syncline) { 267 | //if (!vblank_found_chipset) { 268 | if (is_syncline > 0) { 269 | var rpt = SAEF_now(); 270 | var v = rpt - vsyncmintime; 271 | var v2 = rpt - is_syncline_end; 272 | if (v > vsynctimebase || v < -vsynctimebase) v = 0; 273 | if (v < 0 && v2 < 0) { 274 | pissoff = PISSOFF_NOJIT_VALUE; 275 | return; 276 | } 277 | } else if (is_syncline < 0) { 278 | var rpt = SAEF_now(); 279 | var v = rpt - is_syncline_end; 280 | if (v < 0) { 281 | pissoff = PISSOFF_NOJIT_VALUE; 282 | return; 283 | } 284 | } 285 | //} 286 | is_syncline = 0; 287 | } 288 | 289 | cycles_to_add -= nextevent - SAEV_Events_currcycle; 290 | SAEV_Events_currcycle = nextevent; 291 | 292 | for (var i = 0; i < EV_MAX; i++) { 293 | if (eventtab[i].active && eventtab[i].evtime == SAEV_Events_currcycle) { 294 | /*if (eventtab[i].handler === null) { 295 | SAEF_error("events.eventtab[%d].handler is null!", i); 296 | eventtab[i].active = false; 297 | } else*/ 298 | eventtab[i].handler(); 299 | } 300 | } 301 | this.schedule(); 302 | } 303 | SAEV_Events_currcycle += cycles_to_add; 304 | } 305 | this.do_cycles_post = function(cycles, v) { 306 | this.do_cycles(cycles); 307 | } 308 | 309 | /*-----------------------------------------------------------------------*/ 310 | 311 | function MISC_handler() { 312 | var mintime = SAEC_Events_CYCLE_MAX; 313 | var ct = SAEV_Events_currcycle; 314 | 315 | if (recursive) { 316 | dorecheck = true; 317 | return; 318 | } 319 | recursive++; 320 | SAER_Events_eventtab[EV_MISC].active = false; 321 | 322 | var recheck = true; 323 | while (recheck) { 324 | recheck = false; 325 | mintime = SAEC_Events_CYCLE_MAX; 326 | for (var i = 0; i < EV2_MAX; i++) { 327 | if (eventtab2[i].active) { 328 | if (eventtab2[i].evtime == ct) { 329 | eventtab2[i].active = false; 330 | eventtab2[i].handler(eventtab2[i].data); 331 | if (dorecheck || eventtab2[i].active) { 332 | recheck = true; 333 | dorecheck = false; 334 | } 335 | } else { 336 | var eventtime = eventtab2[i].evtime - ct; 337 | if (eventtime < mintime) 338 | mintime = eventtime; 339 | } 340 | } 341 | } 342 | } 343 | if (mintime != SAEC_Events_CYCLE_MAX) { 344 | SAER_Events_eventtab[EV_MISC].active = true; 345 | SAER_Events_eventtab[EV_MISC].oldcycles = ct; 346 | SAER_Events_eventtab[EV_MISC].evtime = ct + mintime; 347 | SAER.events.schedule(); 348 | } 349 | recursive--; 350 | } 351 | 352 | this.event2_newevent_xx = function(no, t, data, func) { 353 | var et = SAEV_Events_currcycle + t; 354 | if (no < 0) { 355 | no = nextno; 356 | for (;;) { 357 | if (!eventtab2[no].active) 358 | break; 359 | if (eventtab2[no].evtime == et && eventtab2[no].data == data && eventtab2[no].handler === func) 360 | break; 361 | no++; 362 | if (no == EV2_MAX) 363 | no = EV2_MISC; 364 | if (no == nextno) { 365 | SAEF_error("events.event2_newevent_xx() out of events!"); 366 | return; 367 | } 368 | } 369 | nextno = no; 370 | } 371 | eventtab2[no].active = true; 372 | eventtab2[no].evtime = et; 373 | eventtab2[no].handler = func; 374 | eventtab2[no].data = data; 375 | MISC_handler(); 376 | } 377 | function event2_newevent_x(no, t, data, func) { 378 | if (t <= 0) { 379 | func(data); 380 | return; 381 | } 382 | SAER.events.event2_newevent_xx(no, t * SAEC_Events_CYCLE_UNIT, data, func); 383 | } 384 | this.event2_newevent = function(no, t, data) { 385 | event2_newevent_x(no, t, data, eventtab2[no].handler); 386 | } 387 | /*this.event2_newevent2 = function(t, data, func) { 388 | event2_newevent_x(-1, t, data, func); 389 | }*/ 390 | 391 | this.event2_remevent = function(no) { 392 | eventtab2[no].active = false; 393 | } 394 | 395 | /*-----------------------------------------------------------------------*/ 396 | /* events dmal */ 397 | 398 | function dmal_emu(v) { 399 | // Disk and Audio DMA bits are ignored by Agnus, Agnus only checks DMAL and master bit 400 | if (!(SAEV_Custom_dmacon & SAEC_Custom_DMAF_DMAEN)) 401 | return; 402 | 403 | var hpos = SAER.events.current_hpos(); 404 | if (v >= 6) { 405 | v -= 6; 406 | var nr = v >> 1; 407 | var pt = SAER.audio.getpt(nr, (v & 1) != 0); 408 | var dat = SAER_Memory_chipGet16_indirect(pt); 409 | SAEV_Custom_last_value = dat; 410 | SAER.audio.AUDxDAT(nr, dat); 411 | } else { 412 | var w = v & 1; 413 | var pt = SAER.disk.getpt(); 414 | // disk_fifostatus() needed in >100% disk speed modes 415 | if (w) { 416 | // write to disk 417 | if (SAER.disk.fifostatus() <= 0) { 418 | var dat = SAER_Memory_chipGet16_indirect(pt); 419 | SAEV_Custom_last_value = dat; 420 | SAER.disk.DSKDAT(dat); 421 | } 422 | } else { 423 | // read from disk 424 | if (SAER.disk.fifostatus() >= 0) { 425 | var dat = SAER.disk.DSKDATR(); 426 | SAER_Memory_chipPut16_indirect(pt, dat); 427 | } 428 | } 429 | } 430 | } 431 | 432 | this.events_dmal_hsync = function() { 433 | if (dmal) SAEF_error("events.events_dmal_hsync() DMAL error!? %04x", dmal); 434 | dmal = SAER.audio.dmal(); 435 | dmal = (dmal << 6) & 0xffff; 436 | dmal |= SAER.disk.dmal(); 437 | if (dmal) { 438 | dmal_hpos = 0; 439 | //SAER.events.event2_newevent2(7, 13, function(v) { 440 | SAER.events.event2_newevent_xx(-1, 7 * SAEC_Events_CYCLE_UNIT, 13, function(v) { 441 | while (dmal) { 442 | if (dmal & 3) 443 | dmal_emu(dmal_hpos + ((dmal & 2) ? 1 : 0)); 444 | dmal_hpos += 2; 445 | dmal >>>= 2; 446 | } 447 | }); 448 | } 449 | } 450 | 451 | /*-----------------------------------------------------------------------*/ 452 | /* fps counter */ 453 | 454 | function fpscounter_reset() { //global 455 | fps_mavg.clr(); 456 | idle_mavg.clr(); 457 | SAEV_Events_bogusframe = 2; 458 | SAEV_Events_timeframes = 0; 459 | lastframetime = SAEF_now(); 460 | idletime = 0; 461 | } 462 | 463 | this.fpscounter = function(frameok) { 464 | var now = SAEF_now(); 465 | var last = now - lastframetime; 466 | lastframetime = now; 467 | 468 | if (SAEV_Events_bogusframe || last < 0) 469 | return; 470 | 471 | fps_mavg.set(last); 472 | idle_mavg.set(idletime); 473 | idletime = 0; 474 | 475 | frametime += last; 476 | SAEV_Events_timeframes++; 477 | 478 | if ((SAEV_Events_timeframes & 7) == 0) { 479 | var avg = idle_mavg.get(); 480 | var idle = 100.0 - (avg == 0 ? 0 : avg * 100 / vsynctimebase); 481 | if (idle < 0) 482 | idle = 0.0; 483 | else if (idle > 100) 484 | idle = 100.0; 485 | 486 | avg = fps_mavg.get(); 487 | var fps = avg == 0 ? 0 : SAEC_Events_syncbase / avg; 488 | if (fps > 999) 489 | fps = 999.0; 490 | 491 | if (SAEV_Playfield_fake_vblank_hz > fps) idle *= SAEV_Playfield_fake_vblank_hz / fps; 492 | //if (currprefs.turbo_emulation && idle < 100) idle = 100.0; 493 | 494 | SAER.gui.data.fps = fps; 495 | //SAER.gui.data.idle = (int)idle; 496 | SAER.gui.data.idle = idle; 497 | SAER.gui.data.fps_color = frameok ? 0 : 1; 498 | if ((SAEV_Events_timeframes & 15) == 0) { 499 | //SAER.gui.fps(fps, (int)idle, frameok ? 0 : 1); 500 | SAER.gui.fps(fps, idle, frameok ? 0 : 1); 501 | } 502 | } 503 | } 504 | 505 | /*-----------------------------------------------------------------------*/ 506 | /* synchronization */ 507 | 508 | this.framewait2_maximum = function(ll) { 509 | //static int sleeps_remaining; 510 | //if (is_last_line()) { 511 | if (ll) { 512 | /*sleeps_remaining = (165 - currprefs.cpu_idle) / 6; 513 | if (sleeps_remaining < 0) 514 | sleeps_remaining = 0; 515 | // really last line, just run the cpu emulation until whole vsync time has been used 516 | if (SAER.m68k.stopped && currprefs.cpu_idle) { 517 | // CPU in STOP state: sleep if enough time left. 518 | var rpt = SAEF_now(); 519 | while (!vsync_isdone () && ~~vsyncmintime - ~~(rpt + vsynctimebase / 10) > 0 && ~~vsyncmintime - ~~rpt < vsynctimebase) { 520 | //if (!execute_other_cpu(rpt + vsynctimebase / 10)) 521 | SAEF_sleep(1); 522 | rpt = SAEF_now(); 523 | } 524 | } else*/ if (SAEV_config.cpu.speedThrottle) { 525 | vsyncmintime = SAEF_now(); // end of CPU emulation time 526 | is_syncline = 0; 527 | } else { 528 | vsyncmintime = vsyncmaxtime; // emulate if still time left 529 | is_syncline_end = SAEF_now() + vsynctimebase; // far enough in future, we never wait that long 530 | is_syncline = 2; 531 | } 532 | } else { 533 | //static int linecounter; 534 | // end of scanline, run cpu emulation as long as we still have time 535 | vsyncmintime += vsynctimeperline; 536 | linecounter++; 537 | is_syncline = 0; 538 | //if (!vsync_isdone() && !currprefs.turbo_emulation) 539 | { 540 | if (vsyncmaxtime - vsyncmintime > 0) { 541 | if (vsyncwaittime - vsyncmintime > 0) { 542 | var rpt = SAEF_now(); 543 | // Extra time left? Do some extra CPU emulation 544 | if (vsyncmintime - rpt > 0) { 545 | /*if (SAER.m68k.stopped && currprefs.cpu_idle && sleeps_remaining > 0) { 546 | // STOP STATE: sleep. 547 | SAEF_sleep(1); 548 | sleeps_remaining--; 549 | } else*/ { 550 | is_syncline = 1; 551 | // limit extra time 552 | is_syncline_end = rpt + vsynctimeperline; 553 | linecounter = 0; 554 | } 555 | } 556 | } 557 | if (!SAER_Playfield_isvsync()) { 558 | // extra cpu emulation time if previous 10 lines without extra time. 559 | if (!is_syncline && linecounter >= 10 && (!SAER.m68k.stopped)) { // || !currprefs.cpu_idle)) { 560 | is_syncline = -1; 561 | is_syncline_end = SAEF_now() + vsynctimeperline; 562 | linecounter = 0; 563 | } 564 | } 565 | } 566 | } 567 | } 568 | } 569 | this.framewait2_normal = function() { 570 | vsyncmintime += vsynctimeperline; 571 | //if (!vsync_isdone() && !currprefs.turbo_emulation) 572 | { 573 | var rpt = SAEF_now(); 574 | // sleep if more than 2ms "free" time 575 | //while (!vsync_isdone() && vsyncmintime - Math.floor(rpt + vsynctimebase / 10) > 0 && vsyncmintime - rpt < vsynctimebase) { 576 | while (vsyncmintime - Math.floor(rpt + vsynctimebase / 10) > 0 && vsyncmintime - rpt < vsynctimebase) { 577 | //if (!execute_other_cpu(rpt + vsynctimebase / 10)) 578 | SAEF_sleep(1); 579 | rpt = SAEF_now(); 580 | //SAEF_log("*"); 581 | } 582 | } 583 | } 584 | 585 | /*---------------------------------*/ 586 | 587 | function rpt_vsync(adjust) { 588 | var curr_time = SAEF_now(); 589 | var v = curr_time - vsyncwaittime + adjust; 590 | if (v > SAEC_Events_syncbase || v < -SAEC_Events_syncbase) { 591 | vsyncmintime = vsyncmaxtime = vsyncwaittime = curr_time; 592 | v = 0; 593 | } 594 | return v; 595 | } 596 | /*function rtg_vsync() { 597 | #ifdef PICASSO96 598 | var start = SAEF_now(); 599 | picasso_handle_vsync(); 600 | var end = SAEF_now(); 601 | SAEV_Events_frameskiptime += end - start; 602 | #endif 603 | } 604 | function rtg_vsynccheck() { 605 | if (vblank_found_rtg) { 606 | vblank_found_rtg = false; 607 | rtg_vsync(); 608 | } 609 | }*/ 610 | 611 | this.framewait = function() { 612 | var curr_time; 613 | var start; 614 | var vs = SAER_Playfield_isvsync_chipset(); 615 | var status = 0; 616 | 617 | is_syncline = 0; 618 | 619 | //static struct mavg_data ma_frameskipt; 620 | var frameskipt_avg = ~~ma_frameskipt.set(SAEV_Events_frameskiptime); SAEV_Events_frameskiptime = 0; 621 | var reflowt_avg = ~~ma_reflowt.set(SAEV_Events_reflowtime); 622 | 623 | /*OWN stripped 624 | if (vs > 0) {} else if (vs < 0) {}*/ 625 | 626 | status = 1; 627 | 628 | var clockadjust = 0; 629 | var vstb = vsynctimebase; 630 | 631 | if (SAEV_config.cpu.speed < 0) { //max 632 | if (!SAEV_Playfield_frame_rendered && !SAEV_Playfield_picasso_on) 633 | SAEV_Playfield_frame_rendered = SAER.video.render_screen(false); 634 | 635 | if (SAEV_config.cpu.speedThrottle) { 636 | // this delay can safely overshoot frame time by 1-2 ms, following code will compensate for it. 637 | for (;;) { 638 | curr_time = SAEF_now(); 639 | if (vsyncwaittime - curr_time <= 0 || vsyncwaittime - curr_time > 2 * vsynctimebase) 640 | break; 641 | //rtg_vsynccheck(); 642 | SAEF_sleep(1); 643 | } 644 | } else 645 | curr_time = SAEF_now(); 646 | 647 | var adjust = 0, max; 648 | if (curr_time - vsyncwaittime > 0 && curr_time - vsyncwaittime < (vstb >> 1)) 649 | adjust += curr_time - vsyncwaittime; 650 | adjust += clockadjust; 651 | if (SAEV_config.cpu.speedThrottle) 652 | max = Math.truncate(vstb * (1000.0 + SAEV_config.cpu.speedThrottle) / 1000.0 - adjust); 653 | else 654 | max = vstb - adjust; 655 | vsyncwaittime = curr_time + vstb - adjust; 656 | vsyncmintime = curr_time; 657 | 658 | if (max < 0) { 659 | max = 0; 660 | vsynctimeperline = 1; 661 | } else 662 | vsynctimeperline = max / (SAER.playfield.get_maxvpos_display() + 1) >>> 0; 663 | 664 | vsyncmaxtime = curr_time + max; 665 | 666 | //SAEF_info("%06d:%06d/%06d", adjust, vsynctimeperline, vstb); 667 | } else { 668 | const syncbase1000inv = 1.0 / (SAEC_Events_syncbase / 1000); //OWN 669 | var t = reflowt_avg; //OWN 670 | 671 | if (!SAEV_Playfield_frame_rendered && !SAEV_Playfield_picasso_on) { 672 | start = SAEF_now(); 673 | SAEV_Playfield_frame_rendered = SAER.video.render_screen(false); 674 | t += SAEF_now() - start; 675 | } 676 | start = SAEF_now(); 677 | while (true) { //while (!currprefs.turbo_emulation) { 678 | var v = rpt_vsync(clockadjust) * syncbase1000inv; //double 679 | if (v >= -4) break; 680 | //rtg_vsynccheck(); 681 | SAEF_sleep(2); 682 | } 683 | while (rpt_vsync(clockadjust) < 0) { 684 | //rtg_vsynccheck(); 685 | } 686 | curr_time = SAEF_now(); 687 | idletime += curr_time - start; 688 | 689 | vsyncmintime = curr_time; 690 | vsyncmaxtime = vsyncwaittime = curr_time + vstb; 691 | 692 | if (SAEV_Playfield_frame_rendered) { 693 | SAER.video.show_screen(0); 694 | t += SAEF_now() - curr_time; 695 | } 696 | t += frameskipt_avg; 697 | vsynctimeperline = ~~((vstb - t) / 3); 698 | if (vsynctimeperline < 0) 699 | vsynctimeperline = 0; 700 | else if (vsynctimeperline > vstb / 3 >>> 0) 701 | vsynctimeperline = vstb / 3 >>> 0; 702 | 703 | SAEV_Playfield_frame_shown = true; 704 | } 705 | return status != 0; 706 | } 707 | 708 | /*-----------------------------------------------------------------------*/ 709 | /* exact cycling */ 710 | 711 | /*this.alloc_cycle = function(hpos, type) { 712 | //#ifdef CPUEMU_13 713 | //#if 0 714 | //if (cycle_line[hpos]) SAEF_log("events.alloc_cycle() hpos=%d, old=%d, new=%d", hpos, cycle_line[hpos], type); 715 | //if ((type == SAEC_Events_cycle_line_COPPER) && (hpos & 1) && hpos != SAER.playfield.get_maxhpos() - 2) SAEF_log("events.alloc_cycle() odd %d cycle %d", hpos); 716 | //if (!(hpos & 1) && (type == SAEC_Events_cycle_line_SPRITE || type == SAEC_Events_cycle_line_REFRESH || type == SAEC_Events_cycle_line_MISC)) SAEF_log("events.alloc_cycle() even %d cycle %d", type, hpos); 717 | //#endif 718 | cycle_line[hpos] = type; 719 | //#endif 720 | } 721 | this.alloc_cycle_maybe = function(hpos, type) { 722 | if ((cycle_line[hpos] & SAEC_Events_cycle_line_MASK) == 0) 723 | this.alloc_cycle(hpos, type); 724 | } 725 | this.alloc_cycle_blitter = function(hpos, ptr, chnum) { 726 | if (cycle_line[hpos] & SAEC_Events_cycle_line_COPPER_SPECIAL) { 727 | //static int warned = 100; 728 | var srcptr = SAER.copper.get_copxlc(); //cop_state.strobe == 1 ? cop1lc : cop2lc; 729 | //if (warned > 0) 730 | { 731 | SAEF_warn("events.alloc_cycle_blitter() buggy copper cycle conflict with blitter ch %d", chnum); 732 | //warned--; 733 | } 734 | //if ((currprefs.cs_hacks & 1) && SAEV_config.cpu.model == SAEC_Config_CPU_Model_68000) 735 | if (SAEV_config.cpu.model == SAEC_Config_CPU_Model_68000 && SAEV_config.chipset.blitter.cycle_exact) 736 | ptr.val = srcptr; 737 | } 738 | this.alloc_cycle(hpos, SAEC_Events_cycle_line_BLITTER); 739 | }*/ 740 | } 741 | -------------------------------------------------------------------------------- /sae/filesys.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | 20 | function SAEO_Filesys() { 21 | this.uci_set_defaults = function(uci, rdb) { 22 | //memset(uci, 0, sizeof(struct uaedev_config_info)); 23 | //controller 24 | //if (uci.controller_type == 0) 25 | uci.controller_type = SAEC_Config_Mount_Controller_Type_MB_IDE; 26 | uci.controller_unit = 0; //IDE channel + unit 27 | uci.controller_media_type = 0; // 1 = CF IDE, 0 = normal 28 | uci.unit_feature_level = SAEC_Config_Mount_Controller_Level_ATA_2; 29 | uci.unit_special_flags = 0; //1 = force LBA48 30 | //file 31 | //uci.type = UAEDEV_HDF; 32 | //uci.file.name = ""; 33 | //uci.file.size = 0; 34 | //uci.file.data = ""; 35 | uci.readonly = false; 36 | //rdb 37 | //drive geometry 38 | uci.blocksize = 512; 39 | uci.cyls = 0; // calculated/corrected highcyl 40 | if (!rdb) { 41 | uci.surfaces = 1; 42 | uci.sectors = 32; 43 | } 44 | //partition 45 | uci.bootable = true; 46 | uci.automount = true; 47 | uci.unit = 0; 48 | uci.flags = 0; 49 | uci.devname = ""; 50 | //DosEnvec 51 | uci.sectorsperblock = 1; 52 | if (!rdb) 53 | uci.reserved = 2; 54 | uci.interleave = false; 55 | uci.lowcyl = 0; 56 | uci.highcyl = 0; // zero if detected from size 57 | uci.buffers = 50; 58 | uci.bufmemtype = 1; 59 | uci.maxtransfer = 0x7fffffff; 60 | uci.mask = 0xffffffff; 61 | uci.bootpri = 0; 62 | uci.dostype = 0x444f5301; 63 | //filesystem 64 | uci.filesys = ""; 65 | //DeviceNode 66 | uci.stacksize = 4000; 67 | uci.priority = -129; 68 | //uci.device_emu_unit = -1; 69 | } 70 | 71 | /*-----------------------------------------------------------------------*/ 72 | 73 | function allocuci(p, nr, idx, unitnum) { 74 | if (typeof unitnum == "undefined") 75 | unitnum = -1; 76 | var uci = p.mount.config[nr]; 77 | if (idx >= 0) { 78 | /*var ui = mountinfo.ui[idx]; 79 | ui.configureddrive = 1;*/ 80 | 81 | uci.configoffset = idx; 82 | uci.unitnum = unitnum; 83 | } else { 84 | uci.configoffset = -1; 85 | uci.unitnum = -1; 86 | } 87 | } 88 | 89 | /*---------------------------------*/ 90 | 91 | function getunittype(uci) { 92 | return "HD" //uci.type == UAEDEV_CD ? "CD" : (uci.type == UAEDEV_TAPE ? "TAPE" : "HD"); 93 | } 94 | function ismainboardide() { 95 | return SAEV_config.chipset.ide != 0; 96 | } 97 | /*function isa3000scsi() { 98 | return SAEV_config.chipset.mbdmac == 1; 99 | } 100 | function isa4000tscsi() { 101 | return SAEV_config.chipset.mbdmac == 2; 102 | } 103 | function iscdtvscsi() { 104 | return currprefs.cs_cdtvscsi != 0; 105 | }*/ 106 | function add_mainboard_unit_init() { 107 | if (ismainboardide()) { 108 | SAEF_log("filesys.add_mainboard_unit_init() Initializing mainboard IDE"); 109 | SAER.gayle.gayle_add_ide_unit(-1, null); 110 | } 111 | /*if (isa3000scsi()) { 112 | SAEF_log("filesys.add_mainboard_unit_init() Initializing A3000 mainboard SCSI"); 113 | a3000_add_scsi_unit(-1, null, null); 114 | } 115 | if (isa4000tscsi()) { 116 | SAEF_log("filesys.add_mainboard_unit_init() Initializing A4000T mainboard SCSI"); 117 | a4000t_add_scsi_unit(-1, null, null); 118 | } 119 | if (iscdtvscsi()) { 120 | SAEF_log("filesys.add_mainboard_unit_init() Initializing CDTV SCSI expansion"); 121 | cdtv_add_scsi_unit(-1, null, null); 122 | }*/ 123 | } 124 | 125 | function add_ide_unit(type, unit, uci) { 126 | var added = false; 127 | if (type == SAEC_Config_Mount_Controller_Type_MB_IDE) { 128 | if (ismainboardide()) { 129 | SAEF_log("filesys.add_ide_unit() Adding mainboard IDE %s unit %d ('%s')", getunittype(uci), unit, uci.file.name); 130 | SAER.gayle.gayle_add_ide_unit(unit, uci); 131 | added = true; 132 | } 133 | } 134 | return added; 135 | } 136 | 137 | function initialize_mountinfo() { 138 | // init all controllers first 139 | add_mainboard_unit_init(); 140 | 141 | for (var nr = 0; nr < 6; nr++) { 142 | var uci = SAEV_config.mount.config[nr].ci; 143 | var type = uci.controller_type; 144 | var unit = uci.controller_unit; 145 | var added = false; 146 | if (type == 0 || uci.file.size == 0) 147 | continue; 148 | if (type == SAEC_Config_Mount_Controller_Type_MB_IDE) 149 | added = add_ide_unit(type, unit, uci); 150 | else if (type == SAEC_Config_Mount_Controller_Type_PCMCIA_SRAM) { 151 | SAER.gayle.gayle_add_pcmcia_sram_unit(uci); 152 | added = true; 153 | } 154 | else if (type == SAEC_Config_Mount_Controller_Type_PCMCIA_IDE) { 155 | SAER.gayle.gayle_add_pcmcia_ide_unit(uci); 156 | added = true; 157 | } 158 | if (added) 159 | allocuci(SAEV_config, nr, -1); 160 | } 161 | } 162 | function free_mountinfo() { 163 | SAER.gayle.free_units(); 164 | } 165 | 166 | /*-----------------------------------------------------------------------*/ 167 | 168 | this.start_threads = function() {} //filesys_start_threads() 169 | 170 | this.cleanup = function() { //filesys_cleanup() 171 | free_mountinfo(); 172 | } 173 | 174 | this.reset = function() { //filesys_reset() 175 | free_mountinfo(); 176 | initialize_mountinfo(); 177 | } 178 | this.prepare_reset = function() {} //filesys_prepare_reset() 179 | } 180 | -------------------------------------------------------------------------------- /sae/m68k.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Notes: 18 | | This file consists of two parts: high-level functions, which are ported 19 | | from WinUAE 3.2.x and low-level functions, written from scratch. 20 | -------------------------------------------------------------------------*/ 21 | /* global constants */ 22 | 23 | //const SAEC_CPU_halt_PPC_ONLY = -1; 24 | const SAEC_CPU_halt_BUS_ERROR_DOUBLE_FAULT = 1; 25 | const SAEC_CPU_halt_DOUBLE_FAULT = 2; 26 | const SAEC_CPU_halt_OPCODE_FETCH_FROM_NON_EXISTING_ADDRESS = 3; 27 | //const SAEC_CPU_halt_ACCELERATOR_CPU_FALLBACK = 4; 28 | //const SAEC_CPU_halt_ALL_CPUS_STOPPED = 5; 29 | //const SAEC_CPU_halt_FAKE_DMA = 6; 30 | const SAEC_CPU_halt_AUTOCONFIG_CONFLICT = 7; 31 | //const SAEC_CPU_halt_PCI_CONFLICT = 8; 32 | //const SAEC_CPU_halt_CPU_STUCK = 9; 33 | 34 | /*---------------------------------*/ 35 | /* global references */ 36 | 37 | /*---------------------------------*/ 38 | /* global variables */ 39 | 40 | /*---------------------------------*/ 41 | 42 | function SAEO_M68K() { 43 | var reset_delay = false; 44 | this.halted = 0; 45 | this.stopped = false; 46 | 47 | var haltloop_prevvpos = false; 48 | var prevtime = false; 49 | 50 | /*-----------------------------------------------------------------------*/ 51 | 52 | this.setup = function() { //init_m68k() 53 | return SAER.cpu.setup(); 54 | } 55 | 56 | function m68k_reset(hardreset) { //m68k_reset2 57 | SAEV_spcflags = 0; 58 | SAEF_setSpcFlags(SAEC_spcflag_CHECK); 59 | 60 | this.halted = 0; 61 | haltloop_prevvpos = false; 62 | SAER.gui.data.cpu_halted = 0; 63 | SAER.gui.led(SAEC_GUI_LED_CPU, 0, -1); 64 | 65 | reset_delay = false; 66 | prevtime = false; 67 | 68 | SAER.cpu.reset(hardreset); 69 | //SAER.cpu.diss(regs.pc, 16); 70 | } 71 | 72 | this.dump = function() { 73 | //var out = ""; 74 | SAER.cpu.dump(); 75 | //SAEF_log(out); 76 | } 77 | 78 | /*-----------------------------------------------------------------------*/ 79 | 80 | this.cpureset = function() { 81 | /* RESET hasn"t increased PC yet, 1 word offset */ 82 | var ksboot = 0xf80002 - 2; 83 | 84 | reset_delay = SAEV_config.cpu.resetDelay; 85 | SAEF_setSpcFlags(SAEC_spcflag_CHECK); 86 | //send_internalevent(INTERNALEVENT_CPURESET); 87 | if (SAEV_config.cpu.compatible && SAEV_config.cpu.model <= SAEC_Config_CPU_Model_68020) { 88 | SAER.playfield.custom_reset(false, false); 89 | return; 90 | } 91 | var pc = SAER_CPU_getPC() + 2; 92 | var bank = SAER_Memory_getBank(pc); 93 | if (bank.check(pc, 2)) { 94 | SAEF_log("cpu.cpureset() PC=%x (%s)...", pc - 2, bank.name); 95 | var ins = SAER_Memory_get16(pc); 96 | SAER.playfield.custom_reset(false, false); 97 | // did memory disappear under us? 98 | if (bank === SAER_Memory_getBank(pc)) 99 | return; 100 | // it did 101 | if ((ins & ~7) == 0x4ed0) { 102 | var addr = SAER_CPU_regs.a[ins & 7]; 103 | if (addr < 0x80000) 104 | addr += 0xf80000; 105 | SAEF_log("cpu.cpureset() reset/jmp combination at %08x emulated -> %x", pc, addr); 106 | SAER.cpu.setPC_normal(addr - 2); 107 | return; 108 | } 109 | } 110 | SAEF_log("cpu.cpureset() PC=%x (%s), invalid memory -> %x.", pc, bank.name, ksboot + 2); 111 | SAER.playfield.custom_reset(false, false); 112 | SAER.cpu.setPC_normal(ksboot); 113 | } 114 | 115 | /*-----------------------------------------------------------------------*/ 116 | 117 | this.cpu_halt = function(id) { 118 | // id < 0: m68k halted, PPC active. 119 | // id > 0: emulation halted. 120 | if (!this.halted) { 121 | SAEF_log("CPU halted: reason = %d PC=%08x", id, SAER_CPU_getPC()); 122 | this.halted = id; 123 | SAER.gui.data.cpu_halted = id; 124 | SAER.gui.led(SAEC_GUI_LED_CPU, 0, -1); 125 | if (id >= 0) { 126 | SAER_CPU_regs.intmask = 7; 127 | SAER_Audio_deactivate(); 128 | } 129 | SAEF_setSpcFlags(SAEC_spcflag_CHECK); 130 | } 131 | } 132 | 133 | function haltloop() { 134 | while (SAER.m68k.halted) { 135 | //SAEF_log("cpu.haltloop()"); 136 | var vpos = SAER.playfield.get_vpos(); 137 | if (vpos == 0 && haltloop_prevvpos) { 138 | haltloop_prevvpos = false; 139 | SAEF_sleep(8); 140 | } 141 | if (vpos) 142 | haltloop_prevvpos = true; 143 | 144 | SAER.events.do_cycles(8 * SAEC_Events_CYCLE_UNIT); 145 | 146 | if (SAEV_spcflags & SAEC_spcflag_COPPER) 147 | SAER.copper.cycle(); 148 | 149 | if (SAEV_spcflags) { 150 | if ((SAEV_spcflags & (SAEC_spcflag_BRK | SAEC_spcflag_MODE_CHANGE))) 151 | return true; 152 | } 153 | } 154 | return false; 155 | } 156 | 157 | /*-----------------------------------------------------------------------*/ 158 | 159 | var cpu_keyboardreset = false; 160 | var cpu_hardreset = true; 161 | 162 | this.is_keyboardreset = function() { 163 | return cpu_keyboardreset; 164 | } 165 | this.is_hardreset = function() { 166 | return cpu_hardreset; 167 | } 168 | 169 | this.m68k_pause = function() { 170 | if (SAEV_command == SAEC_command_Pause) { 171 | SAEV_command = 0; 172 | if (!SAER.paused) { 173 | SAEF_log("->pause"); 174 | SAER.paused = true; 175 | SAER.pause_program(1); 176 | } 177 | } 178 | else if (SAEV_command == SAEC_command_Resume) { 179 | SAEV_command = 0; 180 | if (SAER.paused) { 181 | SAEF_log("->resume"); 182 | SAER.pause_program(0); 183 | SAER.paused = false; 184 | prevtime = false; 185 | } 186 | } 187 | else if (SAEV_command == -SAEC_command_Quit || 188 | SAEV_command == -SAEC_command_Reset || 189 | SAEV_command == -SAEC_command_KeyboardReset || 190 | SAEV_command == -SAEC_command_HardReset 191 | ) { 192 | SAEF_log("->stop"); 193 | SAER.paused = false; 194 | } 195 | 196 | if (SAER.paused) 197 | setTimeout(function() { SAER.m68k.m68k_pause(); }, 500); 198 | else 199 | setTimeout(function() { SAER.m68k.m68k_cycle(0, 0); }, 0); 200 | } 201 | 202 | this.m68k_cycle = function(hardboot, startup) { 203 | try { 204 | if (SAEV_command > 0) { 205 | cpu_keyboardreset = SAEV_command == SAEC_command_KeyboardReset; 206 | cpu_hardreset = ((SAEV_command == SAEC_command_HardReset ? 1 : 0) | hardboot) != 0; 207 | 208 | if (SAEV_command == SAEC_command_Quit) { 209 | this.m68k_gone(); 210 | return; 211 | } 212 | else if (SAEV_command == SAEC_command_Pause) { 213 | this.m68k_pause(); 214 | return; 215 | } 216 | 217 | SAEV_command = 0; 218 | hardboot = 0; 219 | 220 | SAEV_Events_hsync_counter = 0; 221 | SAEV_Events_vsync_counter = 0; 222 | SAEV_Events_currcycle = 0; SAER_Events_eventtab[SAEC_Events_EV_HSYNC].oldcycles = 0; 223 | 224 | SAER.playfield.custom_reset(cpu_hardreset, cpu_keyboardreset); 225 | m68k_reset(cpu_hardreset); 226 | if (cpu_hardreset) { 227 | SAER.memory.clear(); 228 | SAEF_log("m68k.m68k_cycle() hardreset, memory cleared."); 229 | } 230 | 231 | if (SAEV_config.audio.mode == 0) 232 | SAER_Events_eventtab[SAEC_Events_EV_AUDIO].active = false; 233 | 234 | SAER.cpu.setPC_normal(SAER_CPU_regs.pc); 235 | 236 | //SAER.audio.check_prefs_changed_audio(); 237 | 238 | //statusline_clear(); 239 | 240 | if (typeof SAEV_config.hook.event.reseted === "function") 241 | SAEV_config.hook.event.reseted(cpu_hardreset); 242 | 243 | cpu_hardreset = false; 244 | } 245 | 246 | if (startup) { 247 | SAER.playfield.custom_prepare(); 248 | //protect_roms(true); 249 | startup = 0; 250 | } 251 | SAEF_clrSpcFlags(SAEC_spcflag_MODE_CHANGE); 252 | 253 | if (this.halted) { 254 | this.cpu_halt(this.halted); 255 | /*if (this.halted < 0) { 256 | haltloop(); 257 | //continue; 258 | setTimeout(function() { SAER.m68k.m68k_cycle(hardboot, startup); }, 0); 259 | return; 260 | }*/ 261 | } 262 | 263 | if (prevtime !== false) // && SAEV_config.cpu.speed >= 0) 264 | SAEV_Events_reflowtime = SAEF_now() - prevtime; 265 | 266 | SAER_CPU_run_func(); 267 | 268 | //if (SAEV_config.cpu.speed >= 0) 269 | prevtime = SAEF_now(); 270 | 271 | setTimeout(function() { SAER.m68k.m68k_cycle(hardboot, startup); }, 0); 272 | } catch(e) { 273 | this.m68k_gone(); 274 | if (e instanceof SAEO_Error) { 275 | if (typeof SAEV_config.hook.log.error === "function") 276 | SAEV_config.hook.log.error(e.err, e.msg); 277 | else 278 | alert(e.msg); 279 | } else 280 | throw e; 281 | } 282 | } 283 | this.m68k_go = function(may_quit) { 284 | var hardboot = 1; 285 | var startup = 1; 286 | 287 | //SAEF_info("m68k.m68k_go()"); 288 | SAER.events.reset_frame_rate_hack(); 289 | 290 | SAER.running = true; 291 | //this.m68k_cycle(1, 1); 292 | setTimeout(function() { SAER.m68k.m68k_cycle(hardboot, startup); }, 0); 293 | } 294 | this.m68k_gone = function() { 295 | //SAEF_info("m68k.m68k_gone()"); 296 | //protect_roms(false); 297 | SAER.running = false; 298 | 299 | SAER.leave_program(); 300 | } 301 | 302 | /*-----------------------------------------------------------------------*/ 303 | 304 | this.m68k_setstopped = function() { 305 | this.stopped = true; 306 | /* A traced STOP instruction drops through immediately without actually stopping. */ 307 | if ((SAEV_spcflags & SAEC_spcflag_DOTRACE) == 0) 308 | SAEF_setSpcFlags(SAEC_spcflag_STOP); 309 | else 310 | this.m68k_resumestopped(); 311 | } 312 | 313 | this.m68k_resumestopped = function() { 314 | if (this.stopped) { 315 | this.stopped = false; 316 | SAER_CPU_fill_prefetch(); 317 | SAEF_clrSpcFlags(SAEC_spcflag_STOP); 318 | } 319 | } 320 | 321 | /*-----------------------------------------------------------------------*/ 322 | 323 | this.doint = function() { 324 | if (SAEV_config.cpu.compatible && SAEV_config.cpu.model < SAEC_Config_CPU_Model_68020) 325 | SAEF_setSpcFlags(SAEC_spcflag_INT); 326 | else 327 | SAEF_setSpcFlags(SAEC_spcflag_DOINT); 328 | } 329 | 330 | this.doint_trace = function(t) { //OWN cpu.setSR() 331 | //this.doint(); 332 | if (SAEV_config.cpu.compatible && SAEV_config.cpu.model < SAEC_Config_CPU_Model_68020) 333 | SAEF_setSpcFlags(SAEC_spcflag_INT); 334 | else 335 | SAEF_setSpcFlags(SAEC_spcflag_DOINT); 336 | if (t) 337 | SAEF_setSpcFlags(SAEC_spcflag_TRACE); 338 | else 339 | SAEF_clrSpcFlags(SAEC_spcflag_TRACE); 340 | } 341 | 342 | /*-----------------------------------------------------------------------*/ 343 | 344 | function do_interrupt(nr) { 345 | this.stopped = false; 346 | SAEF_clrSpcFlags (SAEC_spcflag_STOP); 347 | SAEF_assert(nr < 8 && nr >= 0); 348 | 349 | for (;;) { 350 | SAER_CPU_exception(nr + 24); 351 | SAER_CPU_regs.intmask = nr; 352 | if (!SAEV_config.cpu.compatible) 353 | break; 354 | 355 | nr = SAER.custom.intlev(); 356 | if (nr <= 0 || SAER_CPU_regs.intmask >= nr) 357 | break; 358 | } 359 | 360 | SAER.m68k.doint(); 361 | } 362 | /*this.NMI = function() { 363 | do_interrupt(7); 364 | }*/ 365 | 366 | //static uaecptr last_trace_ad = 0; 367 | function do_trace() { 368 | if (SAER_CPU_regs.t0 && SAEV_config.cpu.model >= SAEC_Config_CPU_Model_68020) { 369 | /* should also include TRAP, CHK, SR modification FPcc */ 370 | /* probably never used so why bother */ 371 | /* We can afford this to be inefficient... */ 372 | SAER.cpu.setPC_normal(SAER_CPU_getPC()); 373 | SAER_CPU_fill_prefetch(); 374 | var opcode = SAER_Memory_get16(SAER_CPU_regs.pc); 375 | if (opcode == 0x4e73 /* RTE */ 376 | || opcode == 0x4e74 /* RTD */ 377 | || opcode == 0x4e75 /* RTS */ 378 | || opcode == 0x4e77 /* RTR */ 379 | || opcode == 0x4e76 /* TRAPV */ 380 | || (opcode & 0xffc0) == 0x4e80 /* JSR */ 381 | || (opcode & 0xffc0) == 0x4ec0 /* JMP */ 382 | || (opcode & 0xff00) == 0x6100 /* BSR */ 383 | || ((opcode & 0xf000) == 0x6000 && ccTab[(opcode >> 8) & 0xf]()) /* Bcc */ 384 | || ((opcode & 0xf0f0) == 0x5050 && !ccTab[(opcode >> 8) & 0xf]() && SAER_CPU_regs.d[opcode & 7] & 0xffff != 0) /* DBcc */ 385 | ) { 386 | //last_trace_ad = SAER_CPU_getPC(); 387 | SAEF_clrSpcFlags(SAEC_spcflag_TRACE); 388 | SAEF_setSpcFlags(SAEC_spcflag_DOTRACE); 389 | } 390 | } else if (SAER_CPU_regs.t1) { 391 | //last_trace_ad = SAER_CPU_getPC(); 392 | SAEF_clrSpcFlags(SAEC_spcflag_TRACE); 393 | SAEF_setSpcFlags(SAEC_spcflag_DOTRACE); 394 | } 395 | } 396 | 397 | this.do_specialties = function(cycles) { 398 | if (SAEV_spcflags & SAEC_spcflag_MODE_CHANGE) 399 | return true; 400 | 401 | if (SAEV_spcflags & SAEC_spcflag_CHECK) { 402 | if (this.halted) { 403 | SAEF_clrSpcFlags(SAEC_spcflag_CHECK); 404 | if (haltloop()) 405 | return true; 406 | } 407 | if (reset_delay) { 408 | var vsynccnt = 60; 409 | var vsyncstate = -1; 410 | while (vsynccnt > 0 && SAEV_command == 0) { 411 | SAER.events.do_cycles(8 * SAEC_Events_CYCLE_UNIT); 412 | if (SAEV_spcflags & SAEC_spcflag_COPPER) 413 | SAER.copper.cycle(); 414 | if (SAEV_Events_timeframes != vsyncstate) { 415 | vsyncstate = SAEV_Events_timeframes; 416 | vsynccnt--; 417 | } 418 | } 419 | reset_delay = false; 420 | } 421 | SAEF_clrSpcFlags(SAEC_spcflag_CHECK); 422 | } 423 | 424 | /*#ifdef ACTION_REPLAY 425 | #ifdef ACTION_REPLAY_HRTMON 426 | if ((SAEV_spcflags & SAEC_spcflag_ACTION_REPLAY) && hrtmon_flag != ACTION_REPLAY_INACTIVE) { 427 | int isinhrt = (SAER_CPU_getPC() >= hrtmem_start && SAER_CPU_getPC() < hrtmem_start + hrtmem_size); 428 | if (hrtmon_flag == ACTION_REPLAY_ACTIVE && !isinhrt) 429 | hrtmon_hide (); 430 | if (hrtmon_flag == ACTION_REPLAY_IDLE && isinhrt) 431 | hrtmon_breakenter (); 432 | if (hrtmon_flag == ACTION_REPLAY_ACTIVATE) 433 | hrtmon_enter (); 434 | } 435 | #endif 436 | if ((SAEV_spcflags & SAEC_spcflag_ACTION_REPLAY) && action_replay_flag != ACTION_REPLAY_INACTIVE) { 437 | if (action_replay_flag == ACTION_REPLAY_ACTIVE && !is_ar_pc_in_rom ()) 438 | SAEF_log("PC:%p", SAER_CPU_getPC()); 439 | if (action_replay_flag == ACTION_REPLAY_ACTIVATE || action_replay_flag == ACTION_REPLAY_DORESET) 440 | action_replay_enter (); 441 | if ((action_replay_flag == ACTION_REPLAY_HIDE || action_replay_flag == ACTION_REPLAY_ACTIVE) && !is_ar_pc_in_rom ()) { 442 | action_replay_hide (); 443 | SAEF_clrSpcFlags (SAEC_spcflag_ACTION_REPLAY); 444 | } 445 | if (action_replay_flag == ACTION_REPLAY_WAIT_PC) { 446 | SAEF_log("Waiting for PC: %p, current PC= %p", wait_for_pc, SAER_CPU_getPC()); 447 | if (SAER_CPU_getPC() == wait_for_pc) { 448 | action_replay_flag = ACTION_REPLAY_ACTIVATE; 449 | } 450 | } 451 | } 452 | #endif*/ 453 | 454 | if (SAEV_spcflags & SAEC_spcflag_COPPER) 455 | SAER.copper.cycle(); 456 | 457 | while ((SAEV_spcflags & SAEC_spcflag_BLTNASTY) && SAEF_Custom_dmaen(SAEC_Custom_DMAF_BLTEN) && cycles > 0 && !SAEV_config.chipset.blitter.cycle_exact) { 458 | var c = SAER.blitter.blitnasty(); 459 | if (c < 0) 460 | break; 461 | else if (c > 0) { 462 | cycles -= c * SAEC_Events_CYCLE_UNIT * 2; 463 | if (cycles < SAEC_Events_CYCLE_UNIT) 464 | cycles = 0; 465 | } else 466 | c = 4; 467 | 468 | SAER.events.do_cycles(c * SAEC_Events_CYCLE_UNIT); 469 | if (SAEV_spcflags & SAEC_spcflag_COPPER) 470 | SAER.copper.cycle(); 471 | } 472 | 473 | if (SAEV_spcflags & SAEC_spcflag_DOTRACE) 474 | SAER_CPU_exception(9); 475 | 476 | /*if (SAEV_spcflags & SAEC_spcflag_TRAP) { 477 | SAEF_clrSpcFlags(SAEC_spcflag_TRAP); 478 | SAER_CPU_exception(3); 479 | }*/ 480 | var first = true; 481 | while ((SAEV_spcflags & SAEC_spcflag_STOP) && !(SAEV_spcflags & SAEC_spcflag_BRK)) { 482 | if (!first) SAER.events.do_cycles(4 * SAEC_Events_CYCLE_UNIT); 483 | first = false; 484 | 485 | if (SAEV_spcflags & SAEC_spcflag_COPPER) 486 | SAER.copper.cycle(); 487 | 488 | if (SAEV_spcflags & (SAEC_spcflag_INT | SAEC_spcflag_DOINT)) { 489 | var intr = SAER.custom.intlev(); 490 | SAEF_clrSpcFlags(SAEC_spcflag_INT | SAEC_spcflag_DOINT); 491 | 492 | if (intr > 0 && intr > SAER_CPU_regs.intmask) 493 | do_interrupt(intr); 494 | } 495 | 496 | if (SAEV_spcflags & SAEC_spcflag_MODE_CHANGE) { 497 | this.m68k_resumestopped(); 498 | return true; 499 | } 500 | } 501 | 502 | if (SAEV_spcflags & SAEC_spcflag_TRACE) 503 | do_trace(); 504 | 505 | if (SAEV_spcflags & SAEC_spcflag_INT) { 506 | var intr = SAER.custom.intlev(); 507 | SAEF_clrSpcFlags(SAEC_spcflag_INT | SAEC_spcflag_DOINT); 508 | if (intr > 0 && (intr > SAER_CPU_regs.intmask || intr == 7)) 509 | do_interrupt(intr); 510 | } 511 | if (SAEV_spcflags & SAEC_spcflag_DOINT) { 512 | SAEF_clrSpcFlags(SAEC_spcflag_DOINT); 513 | SAEF_setSpcFlags(SAEC_spcflag_INT); 514 | } 515 | 516 | if (SAEV_spcflags & SAEC_spcflag_BRK) { 517 | SAEF_clrSpcFlags(SAEC_spcflag_BRK); 518 | return true; //OWN 519 | } 520 | 521 | return false; 522 | } 523 | } 524 | -------------------------------------------------------------------------------- /sae/prototypes.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | -------------------------------------------------------------------------*/ 17 | /* Math */ 18 | 19 | if (!Math.truncate) { 20 | Math.truncate = function(v) { 21 | if (v > 0) 22 | return this.floor(v); 23 | else if (v < 0) 24 | return this.ceil(v); 25 | 26 | return 0; 27 | }; 28 | } 29 | 30 | if (!Math.decimalRandom) { 31 | Math.decimalRandom = function() { 32 | //var l = 0, u = 0xffffffff; return this.floor((this.random() * (u - l + 1)) + l); 33 | return (this.random() * 0xffffffff) >>> 0; 34 | }; 35 | } 36 | 37 | /*-----------------------------------------------------------------------*/ 38 | /* Date/Performance */ 39 | 40 | if (!Date.now) { 41 | console.warn("This browser does not support 'Date.now()'. Falling back to 'Date.getTime()'..."); 42 | /* milliseconds since 1 January 1970 00:00:00 UTC */ 43 | Date.now = function() { 44 | return new Date().getTime(); 45 | }; 46 | } 47 | 48 | if (!window.performance) { 49 | console.warn("This browser does not support 'window.performance'. Falling back to 'Date'..."); 50 | window.performance = {}; 51 | } 52 | if (!performance.timing) { 53 | performance.timing = { 54 | navigationStart: Date.now() 55 | }; 56 | } 57 | if (!performance.now) { 58 | if (performance.webkitNow) 59 | performance.now = performance.webkitNow; 60 | else 61 | performance.now = function() { 62 | return Date.now() - this.timing.navigationStart; 63 | }; 64 | } 65 | 66 | /*-----------------------------------------------------------------------*/ 67 | /* AnimationFrame */ 68 | 69 | (function() { 70 | var lastTime = 0; 71 | 72 | if (!window.requestAnimationFrame) { 73 | console.warn("This browser does not support 'window.requestAnimationFrame'. Falling back to 'setTimeout'..."); 74 | window.requestAnimationFrame = function(callback, element) { 75 | var currTime = new Date().getTime(); 76 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 77 | var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); 78 | lastTime = currTime + timeToCall; 79 | return id; 80 | }; 81 | } 82 | if (!window.cancelAnimationFrame) { 83 | console.warn("This browser does not support 'window.cancelAnimationFrame'. Falling back to 'clearTimeout'..."); 84 | window.cancelAnimationFrame = function(id) { 85 | clearTimeout(id); 86 | }; 87 | } 88 | }()); 89 | -------------------------------------------------------------------------------- /sae/rtc.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | /* global variables */ 20 | 21 | var SAEV_RTC_bank = null; 22 | 23 | var SAEV_RTC_delayed_write = 0; 24 | 25 | /*---------------------------------*/ 26 | 27 | function SAEO_RTC() { 28 | const RTC_DEBUG = false; 29 | 30 | var clock_control_d = 0; 31 | var clock_control_e = 0; 32 | var clock_control_f = 0; 33 | 34 | const RF5C01A_RAM_SIZE = 16; 35 | var rtc_memory = null; 36 | var rtc_alarm = null; 37 | 38 | /*---------------------------------*/ 39 | 40 | function localtime(t) { 41 | this.tm_sec = t.getSeconds(); //seconds [0,61] 42 | this.tm_min = t.getMinutes(); //minutes [0,59] 43 | this.tm_hour = t.getHours(); //hour [0,23] 44 | this.tm_mday = t.getDate(); //day of month [1,31] 45 | this.tm_mon = t.getMonth(); //month of year [0,11] 46 | this.tm_year = t.getFullYear() - 1900; //years since 1900 47 | this.tm_wday = t.getDay(); //day of week [0,6] (Sunday = 0) 48 | this.tm_yday = 0; //day of year [0,365] 49 | this.tm_isdst = false; //daylight savings flag 50 | } 51 | 52 | function getct() { 53 | var d = new Date(); 54 | if (SAEV_config.chipset.rtc.adjust) { 55 | var n = d.valueOf(); 56 | d.setTime(n + SAEV_config.chipset.rtc.adjust * 1000); 57 | } 58 | return new localtime(d); 59 | } 60 | 61 | /*---------------------------------*/ 62 | 63 | function getclockreg(addr, ct) { 64 | var v = 0; 65 | 66 | if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_MSM6242B || SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_MSM6242B_A2000) { /* MSM6242B */ 67 | switch (addr) { 68 | case 0x0: v = ct.tm_sec % 10; break; 69 | case 0x1: v = ct.tm_sec / 10 >>> 0; break; 70 | case 0x2: v = ct.tm_min % 10; break; 71 | case 0x3: v = ct.tm_min / 10 >>> 0; break; 72 | case 0x4: v = ct.tm_hour % 10; break; 73 | case 0x5: { 74 | if (clock_control_f & 4) 75 | v = ct.tm_hour / 10 >>> 0; /* 24h */ 76 | else { 77 | v = (ct.tm_hour % 12) / 10 >>> 0; /* 12h */ 78 | v |= ct.tm_hour >= 12 ? 4 : 0; /* AM/PM bit */ 79 | } 80 | break; 81 | } 82 | case 0x6: v = ct.tm_mday % 10; break; 83 | case 0x7: v = ct.tm_mday / 10 >>> 0; break; 84 | case 0x8: v = (ct.tm_mon + 1) % 10; break; 85 | case 0x9: v = (ct.tm_mon + 1) / 10 >>> 0; break; 86 | case 0xA: v = ct.tm_year % 10; break; 87 | case 0xB: v = ((ct.tm_year / 10) >>> 0) & 0x0f; break; 88 | case 0xC: v = ct.tm_wday; break; 89 | case 0xD: v = clock_control_d; break; 90 | case 0xE: v = clock_control_e; break; 91 | case 0xF: v = clock_control_f; break; 92 | } 93 | } else if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_RF5C01A) { /* RF5C01A */ 94 | var bank = clock_control_d & 3; 95 | /* memory access */ 96 | if (bank >= 2 && addr < 0x0d) 97 | return (rtc_memory[addr] >> ((bank == 2) ? 0 : 4)) & 0x0f; 98 | /* alarm */ 99 | if (bank == 1 && addr < 0x0d) { 100 | v = rtc_alarm[addr]; 101 | if (RTC_DEBUG) SAEF_log("rtc.get8(0x%X, ct) ALARM (0x%x)", addr, v); 102 | return v; 103 | } 104 | switch (addr) { 105 | case 0x0: v = ct.tm_sec % 10; break; 106 | case 0x1: v = ct.tm_sec / 10 >>> 0; break; 107 | case 0x2: v = ct.tm_min % 10; break; 108 | case 0x3: v = ct.tm_min / 10 >>> 0; break; 109 | case 0x4: v = ct.tm_hour % 10; break; 110 | case 0x5: { 111 | if (rtc_alarm[10] & 1) 112 | v = ct.tm_hour / 10 >>> 0; /* 24h */ 113 | else { 114 | v = (ct.tm_hour % 12) / 10 >>> 0; /* 12h */ 115 | v |= ct.tm_hour >= 12 ? 2 : 0; /* AM/PM bit */ 116 | } 117 | break; 118 | } 119 | case 0x6: v = ct.tm_wday; break; 120 | case 0x7: v = ct.tm_mday % 10; break; 121 | case 0x8: v = ct.tm_mday / 10 >>> 0; break; 122 | case 0x9: v = (ct.tm_mon + 1) % 10; break; 123 | case 0xA: v = (ct.tm_mon + 1) / 10 >>> 0; break; 124 | case 0xB: v = (ct.tm_year % 100) % 10; break; 125 | case 0xC: v = (ct.tm_year % 100) / 10 >>> 0; break; 126 | case 0xD: v = clock_control_d; break; 127 | case 0xE: v = 0; break; //E and F = write-only, reads as zero 128 | case 0xF: v = 0; break; 129 | } 130 | } 131 | if (RTC_DEBUG) SAEF_log("rtc.get8(0x%X, ct) (0x%x)", addr, v); 132 | return v; 133 | } 134 | 135 | /*---------------------------------*/ 136 | 137 | function read() { //read_battclock() 138 | //if (SAEV_config.chipset.rtc.file.length) 139 | { 140 | var f = null; //SAEF_ZFile_fopen(SAEV_config.chipset.rtc.file, "rb"); 141 | if (f) { 142 | var data = new Uint8Array(16); 143 | SAEF_ZFile_fread(data,0, 16, 1, f); 144 | clock_control_d = data[13]; 145 | clock_control_e = data[14]; 146 | clock_control_f = data[15]; 147 | if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_RF5C01A) { 148 | SAEF_ZFile_fread(rtc_alarm,0, RF5C01A_RAM_SIZE, 1, f); 149 | SAEF_ZFile_fread(rtc_memory,0, RF5C01A_RAM_SIZE, 1, f); 150 | } 151 | SAEF_ZFile_fclose(f); 152 | } 153 | } 154 | } 155 | this.write = function() { //write_battclock() called from cia.vsync() 156 | if (SAEV_config.chipset.rtc.type != SAEC_Config_RTC_Type_None) { 157 | //if (SAEV_config.chipset.rtc.file.length) 158 | { 159 | var f = null; //SAEF_ZFile_fopen(SAEV_config.chipset.rtc.file, "wb"); 160 | if (f) { 161 | var ct = getct(); 162 | var data = new Uint8Array(16); 163 | var od = clock_control_d; 164 | if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_RF5C01A) 165 | clock_control_d &= ~3; 166 | for (var i = 0; i < 13; i++) 167 | data[i] = getclockreg(i, ct); 168 | clock_control_d = od; 169 | data[i] = clock_control_d; 170 | data[i] = clock_control_e; 171 | data[i] = clock_control_f; 172 | SAEF_ZFile_fwrite(data,0, 16, 1, f); 173 | if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_RF5C01A) { 174 | SAEF_ZFile_fwrite(rtc_alarm,0, RF5C01A_RAM_SIZE, 1, f); 175 | SAEF_ZFile_fwrite(rtc_memory,0, RF5C01A_RAM_SIZE, 1, f); 176 | } 177 | SAEF_ZFile_fclose(f); 178 | } 179 | } 180 | } 181 | } 182 | 183 | /*---------------------------------*/ 184 | 185 | this.hardreset = function() { //rtc_hardreset() 186 | switch (SAEV_config.chipset.rtc.type) { 187 | case SAEC_Config_RTC_Type_None: type = "none"; break; 188 | case SAEC_Config_RTC_Type_MSM6242B: type = "MSM6242B"; break; 189 | case SAEC_Config_RTC_Type_RF5C01A: type = "RF5C01A"; break; 190 | case SAEC_Config_RTC_Type_MSM6242B_A2000: type = "MSM6242B A2000"; break; 191 | } 192 | SAEF_log("rtc.hardreset() type '%s'", type); 193 | 194 | SAEV_RTC_delayed_write = 0; 195 | if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_MSM6242B || SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_MSM6242B_A2000) { /* MSM6242B */ 196 | clock_control_d = 0x1; 197 | clock_control_e = 0; 198 | clock_control_f = 0x4; /* 24/12 */ 199 | rtc_memory = null; 200 | rtc_alarm = null; 201 | } else if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_RF5C01A) { /* RF5C01A */ 202 | clock_control_d = 0x8; /* Timer EN */ 203 | clock_control_e = 0; 204 | clock_control_f = 0; 205 | rtc_memory = new Uint8Array(RF5C01A_RAM_SIZE); 206 | rtc_alarm = new Uint8Array(RF5C01A_RAM_SIZE); 207 | for (var i = 0; i < RF5C01A_RAM_SIZE; i++) 208 | rtc_memory[i] = rtc_alarm[i] = 0; 209 | 210 | rtc_alarm[10] = 1; /* 24H mode */ 211 | } 212 | SAEV_RTC_bank.name = "Battery backed up clock ("+type+")"; 213 | read(); 214 | } 215 | 216 | /*---------------------------------*/ 217 | 218 | function get32(addr) { 219 | if ((addr & 0xffff) >= 0x8000 && SAEV_config.chipset.fatGaryRev >= 0) 220 | return SAER.memory.dummyGet(addr, 4, false, 0); 221 | 222 | return ((get16(addr) << 16) | get16(addr + 2)) >>> 0; 223 | } 224 | 225 | function get16(addr) { 226 | if ((addr & 0xffff) >= 0x8000 && SAEV_config.chipset.fatGaryRev >= 0) 227 | return SAER.memory.dummyGet(addr, 2, false, 0); 228 | 229 | return (get8(addr) << 8) | get8(addr + 1); 230 | } 231 | 232 | function get8(addr) { 233 | if ((addr & 0xffff) >= 0x8000 && SAEV_config.chipset.fatGaryRev >= 0) 234 | return SAER.memory.dummyGet(addr, 1, false, 0); 235 | /*#ifdef CDTV 236 | if (currprefs.cs_cdtvram && (addr & 0xffff) >= 0x8000) 237 | return cdtv_battram_read(addr); 238 | #endif*/ 239 | 240 | addr &= 0x3f; 241 | if ((addr & 3) == 2 || (addr & 3) == 0 || SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_None) 242 | return SAER.memory.dummyGet(addr, 1, false, 0); 243 | 244 | var ct = getct(); 245 | return getclockreg(addr >> 2, ct); 246 | } 247 | 248 | function put32(addr, value) { 249 | if ((addr & 0xffff) >= 0x8000 && SAEV_config.chipset.fatGaryRev >= 0) { 250 | SAER.memory.dummyPut(addr, 4, value); 251 | return; 252 | } 253 | put16(addr, value >>> 16); 254 | put16(addr + 2, value & 0xffff); 255 | } 256 | 257 | function put16(addr, value) { 258 | if ((addr & 0xffff) >= 0x8000 && SAEV_config.chipset.fatGaryRev >= 0) { 259 | SAER.memory.dummyPut(addr, 2, value); 260 | return; 261 | } 262 | put8(addr, value >> 8); 263 | put8(addr + 1, value & 0xff); 264 | } 265 | 266 | function put8(addr, value) { 267 | if ((addr & 0xffff) >= 0x8000 && SAEV_config.chipset.fatGaryRev >= 0) { 268 | SAER.memory.dummyPut(addr, 1, value); 269 | return; 270 | } 271 | /*#ifdef CDTV 272 | if (currprefs.cs_cdtvram && (addr & 0xffff) >= 0x8000) { 273 | cdtv_battram_write(addr, value); 274 | return; 275 | } 276 | #endif*/ 277 | 278 | addr &= 0x3f; 279 | if ((addr & 1) != 1 || SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_None) 280 | return; 281 | addr >>= 2; 282 | value &= 0x0f; 283 | if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_MSM6242B || SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_MSM6242B_A2000) { /* MSM6242B */ 284 | if (RTC_DEBUG) SAEF_log("rtc.put8(0x%X, 0x%x)", addr, value); 285 | switch (addr) { 286 | case 0xD: clock_control_d = value & (1|8); break; 287 | case 0xE: clock_control_e = value; break; 288 | case 0xF: clock_control_f = value; break; 289 | } 290 | } else if (SAEV_config.chipset.rtc.type == SAEC_Config_RTC_Type_RF5C01A) { /* RF5C01A */ 291 | var bank = clock_control_d & 3; 292 | /* memory access */ 293 | if (bank >= 2 && addr < 0x0d) { 294 | var ov = rtc_memory[addr]; 295 | rtc_memory[addr] &= ((bank == 2) ? 0xf0 : 0x0f); 296 | rtc_memory[addr] |= value << ((bank == 2) ? 0 : 4); 297 | if (rtc_memory[addr] != ov) SAEV_RTC_delayed_write = -1; 298 | return; 299 | } 300 | /* alarm */ 301 | if (bank == 1 && addr < 0x0d) { 302 | if (RTC_DEBUG) SAEF_log("rtc.put8(0x%X, 0x%x) ALARM", addr, value); 303 | var ov = rtc_alarm[addr]; 304 | rtc_alarm[addr] = value; 305 | rtc_alarm[0] = rtc_alarm[1] = rtc_alarm[9] = rtc_alarm[12] = 0; 306 | rtc_alarm[3] &= ~0x8; 307 | rtc_alarm[5] &= ~0xc; 308 | rtc_alarm[6] &= ~0x8; 309 | rtc_alarm[8] &= ~0xc; 310 | rtc_alarm[10] &= ~0xe; 311 | rtc_alarm[11] &= ~0xc; 312 | if (rtc_alarm[addr] != ov) SAEV_RTC_delayed_write = -1; 313 | return; 314 | } 315 | if (RTC_DEBUG) SAEF_log("rtc.put8(0x%X, 0x%x)", addr, value); 316 | switch (addr) { 317 | case 0xD: clock_control_d = value; break; 318 | case 0xE: clock_control_e = value; break; 319 | case 0xF: clock_control_f = value; break; 320 | } 321 | } 322 | SAEV_RTC_delayed_write = -1; 323 | } 324 | 325 | SAEV_RTC_bank = new SAEO_Memory_addrbank( 326 | get32, get16, get8, 327 | put32, put16, put8, 328 | SAEF_Memory_defaultXLate, SAEF_Memory_defaultCheck, null, null, "Battery backed up clock (none)", 329 | SAEF_Memory_dummyGetInst32, SAEF_Memory_dummyGetInst16, 330 | //SAEC_Memory_addrbank_flag_IO, S_READ, S_WRITE, null, 0x3f, 0xd80000 331 | SAEC_Memory_addrbank_flag_IO, null, 0x3f, 0xd80000 332 | ); 333 | } 334 | -------------------------------------------------------------------------------- /sae/serpar.js: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | | SAE - Scripted Amiga Emulator 3 | | https://github.com/naTmeg/ScriptedAmigaEmulator 4 | | 5 | | Copyright (C) 2012 Rupert Hausberger 6 | | 7 | | This program is free software; you can redistribute it and/or 8 | | modify it under the terms of the GNU General Public License 9 | | as published by the Free Software Foundation; either version 2 10 | | of the License, or (at your option) any later version. 11 | | 12 | | This program is distributed in the hope that it will be useful, 13 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | | GNU General Public License for more details. 16 | | 17 | | Note: ported from WinUAE 3.2.x 18 | -------------------------------------------------------------------------*/ 19 | 20 | function SAEO_Serial() { 21 | const DEBUGIO = 0; /* 0-3 */ 22 | const DEBUGHS = 0; /* 0-2 */ 23 | 24 | var data_in_serdat = 0; /* new data written to SERDAT */ 25 | var data_in_serdatr = 0; /* new data received */ 26 | var data_in_sershift = 0; /* data transferred from SERDAT to shift register */ 27 | var serdatshift = 0; //u16 /* serial shift register */ 28 | var serdatshift_masked = 0; //u16 /* stop bit masked */ 29 | var ovrun = false; 30 | var dtr = false; 31 | var oldserbits = 0; //u8 32 | 33 | var serper = 0, serdat = 0, serdatr = 0; //u16 34 | 35 | /*---------------------------------*/ 36 | 37 | function dochar(v) { 38 | v &= 0xff; 39 | if (v >= 32 && v < 127) 40 | return v; 41 | return 46; //'.' 42 | } 43 | 44 | /*---------------------------------*/ 45 | 46 | function serdatcopy() { 47 | if (data_in_sershift || !data_in_serdat) 48 | return; 49 | serdatshift = serdat; 50 | var bits = ((serdatshift & 0xff80) == 0x80) ? 8 : 7; 51 | serdatshift_masked = serdatshift & ((1 << bits) - 1); 52 | data_in_sershift = 1; 53 | data_in_serdat = 0; 54 | 55 | SAEV_config.hook.serial.put(serdatshift_masked); 56 | 57 | SAER.custom.INTREQ(SAEC_Custom_INTF_SETCLR | SAEC_Custom_INTF_TBE); 58 | } 59 | 60 | this.hsync = function() { //serial_hsynchandler() 61 | if (!data_in_serdatr) { 62 | var ch = SAEV_config.hook.serial.get(); 63 | if (ch > 0) { 64 | ch &= 0xff; //OWN 65 | serdatr = ch | 0x100; 66 | data_in_serdatr = 1; 67 | this.check_irq(); 68 | } 69 | } 70 | 71 | //if (!first_write) return; 72 | 73 | if (data_in_sershift) { 74 | data_in_sershift = 0; 75 | serdatcopy(); 76 | } 77 | } 78 | 79 | this.rbf_clear = function() { //serial_rbf_clear() 80 | ovrun = false; 81 | } 82 | 83 | this.check_irq = function() { //serial_check_irq() 84 | if (data_in_serdatr) 85 | SAER.custom.INTREQ_0(SAEC_Custom_INTF_SETCLR | SAEC_Custom_INTF_RBF); 86 | } 87 | 88 | /*this.uartbreak = function(v) { //serial_uartbreak() 89 | }*/ 90 | 91 | /*---------------------------------*/ 92 | /* CIA access */ 93 | 94 | function status_debug(s) { 95 | SAEF_log("%s DTR=%d RTS=%d CD=%d CTS=%d DSR=%d", s, 96 | (oldserbits & 0x80) ? 0 : 1, (oldserbits & 0x40) ? 0 : 1, 97 | (oldserbits & 0x20) ? 0 : 1, (oldserbits & 0x10) ? 0 : 1, (oldserbits & 0x08) ? 0 : 1); 98 | } 99 | 100 | this.dtr_on = function() { //serial_dtr_on() 101 | if (DEBUGHS > 0) SAEF_log("serial.writestatus() DTR on"); 102 | dtr = true; 103 | } 104 | 105 | this.dtr_off = function() { //serial_dtr_off() 106 | if (DEBUGHS > 0) SAEF_log("serial.writestatus() DTR off"); 107 | dtr = false; 108 | } 109 | 110 | this.readstatus = function(dir) { //serial_readstatus() 111 | var status = 0; 112 | var serbits = oldserbits; //u8 113 | 114 | /*getserstat (&status); 115 | if (!(status & TIOCM_CAR)) { 116 | if (!(serbits & 0x20)) { 117 | serbits |= 0x20; 118 | if (DEBUGHS > 0) SAEF_log("serial.readstatus() CD off"); 119 | } 120 | } else { 121 | if (serbits & 0x20) { 122 | serbits &= ~0x20; 123 | if (DEBUGHS > 0) SAEF_log("serial.readstatus() CD on"); 124 | } 125 | } 126 | if (!(status & TIOCM_DSR)) { 127 | if (!(serbits & 0x08)) { 128 | serbits |= 0x08; 129 | if (DEBUGHS > 0) SAEF_log("serial.readstatus() DSR off"); 130 | } 131 | } else { 132 | if (serbits & 0x08) { 133 | serbits &= ~0x08; 134 | if (DEBUGHS > 0) SAEF_log("serial.readstatus() DSR on"); 135 | } 136 | } 137 | if (!(status & TIOCM_CTS)) { 138 | if (!(serbits & 0x10)) { 139 | serbits |= 0x10; 140 | if (DEBUGHS > 0) SAEF_log("serial.readstatus() CTS off"); 141 | } 142 | } else { 143 | if (serbits & 0x10) { 144 | serbits &= ~0x10; 145 | if (DEBUGHS > 0) SAEF_log("serial.readstatus() CTS on"); 146 | } 147 | }*/ 148 | 149 | serbits &= 0x08 | 0x10 | 0x20; 150 | oldserbits &= ~(0x08 | 0x10 | 0x20); 151 | oldserbits |= serbits; 152 | 153 | if (DEBUGHS > 1) status_debug("serial.readstatus()"); 154 | return oldserbits; 155 | } 156 | 157 | this.writestatus = function(newstate, dir) { //serial_writestatus() 158 | oldserbits &= ~(0x80 | 0x40); 159 | newstate &= 0x80 | 0x40; 160 | oldserbits |= newstate; 161 | 162 | if (DEBUGHS > 1) status_debug("serial.writestatus()"); 163 | return oldserbits; 164 | } 165 | 166 | /*---------------------------------*/ 167 | 168 | this.SERPER = function(w) { 169 | if (!SAEV_config.serial.enabled) 170 | return; 171 | if (serper == w) /* don't set baudrate if it's already ok */ 172 | return; 173 | 174 | serper = w; 175 | //first_write = 1; 176 | 177 | if (DEBUGIO > 1) { 178 | const allowed_baudrates = [ 179 | 0, 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 180 | 19200, 31400, 38400, 57600, 115200, 128000, 256000, -1 181 | ]; 182 | var ninebit = (w & 0x8000) != 0; 183 | w &= 0x7fff; 184 | if (w < 13) w = 13; 185 | var per = w; 186 | if (per == 0) per = 1; 187 | //per = 3546895 / (per + 1); 188 | per = ((SAEV_config.video.ntsc ? SAEC_Playfield_CLOCK_NTSC : SAEC_Playfield_CLOCK_PAL) / (per + 1)) >>> 0; //OWN 189 | if (per == 0) per = 1; 190 | var i = 0; 191 | while (allowed_baudrates[i] >= 0 && per > (allowed_baudrates[i] * 100 / 97) >>> 0) i++; 192 | var baud = allowed_baudrates[i]; 193 | SAEF_log("serial.SERPER() period=%d, baud=%d, bits=%d", w, baud, ninebit ? 9 : 8); 194 | } 195 | } 196 | 197 | this.SERDAT = function(w) { 198 | if (!SAEV_config.serial.enabled) 199 | return; 200 | if (DEBUGIO > 2) SAEF_log("serial.SERDAT() write 0x%04x (%c)", w, dochar(w)); 201 | 202 | serdatcopy(); 203 | 204 | serdat = w; 205 | 206 | if (!w) { 207 | if (DEBUGIO > 1) SAEF_log("serial.SERDAT() zero serial word written?!"); 208 | return; 209 | } 210 | if (DEBUGIO > 1 && data_in_serdat) SAEF_log("serial.SERDAT() program wrote to SERDAT but old byte wasn't fetched yet"); 211 | 212 | data_in_serdat = 1; 213 | serdatcopy(); 214 | } 215 | 216 | this.SERDATR = function() { 217 | if (!SAEV_config.serial.enabled) 218 | return 0x2000; 219 | 220 | serdatr &= 0x03ff; 221 | if (!data_in_serdat) 222 | serdatr |= 0x2000; 223 | if (!data_in_sershift) 224 | serdatr |= 0x1000; 225 | if (data_in_serdatr) 226 | serdatr |= 0x4000; 227 | if (ovrun) 228 | serdatr |= 0x8000; 229 | 230 | if (DEBUGIO > 2) SAEF_log("serial.SERDATR() read 0x%04x (%c)", serdatr, dochar(serdatr)); 231 | 232 | data_in_serdatr = 0; 233 | return serdatr; 234 | } 235 | 236 | /*---------------------------------*/ 237 | 238 | /*this.setup = function() { //serial_init() 239 | }*/ 240 | 241 | this.cleanup = function() { //serial_exit() 242 | dtr = false; 243 | oldserbits = 0; 244 | } 245 | } 246 | 247 | function SAEO_Parallel() { 248 | const DEBUGIO = 0; /* 0-1 */ 249 | const DEBUGHS = 0; /* 0-1 */ 250 | 251 | var status = 4; /* SEL */ 252 | 253 | /*---------------------------------*/ 254 | /* direct */ 255 | 256 | this.direct_write_status = function(v, dir) { 257 | if (DEBUGHS) SAEF_log("parallel.WS(%02x, %02x)", v, dir); 258 | 259 | //status = 0; if ((dir & 4) && !(v & 4)) status = 4; 260 | SAER.cia.parallelack(); 261 | } 262 | 263 | this.direct_read_status = function() { 264 | if (DEBUGHS) SAEF_log("parallel.RS() %02x", status); 265 | 266 | SAER.cia.parallelack(); 267 | return status; 268 | } 269 | 270 | this.direct_write_data = function(v, dir) { 271 | if (DEBUGIO) SAEF_log("parallel.WD(%02x, %02x)", v, dir); 272 | 273 | SAEV_config.hook.parallel.put(v); 274 | SAER.cia.parallelack(); 275 | } 276 | 277 | this.direct_read_data = function() { 278 | var v = SAEV_config.hook.parallel.get() & 0xff; 279 | SAER.cia.parallelack(); 280 | 281 | if (DEBUGIO) SAEF_log("parallel.RD() %02x", v); 282 | return v; 283 | } 284 | 285 | /*---------------------------------*/ 286 | /* printer */ 287 | 288 | /*this.doprinter = function() {}*/ 289 | 290 | /*---------------------------------*/ 291 | 292 | /*this.isprinter = function() { 293 | If enabled parport-joystick can not work, but parport-joystick is not enabled anyway. 294 | 0 = disabled, -1 = direct, 1 = printer 295 | return -1; 296 | }*/ 297 | 298 | /*---------------------------------*/ 299 | 300 | /*this.reset = function() { //initparallel() 301 | }*/ 302 | } 303 | --------------------------------------------------------------------------------