From 0c5b01886621f22e29dc9a0f7d424ac1acaae474 Mon Sep 17 00:00:00 2001 From: Rupert Hausberger Date: Sat, 22 Dec 2012 19:39:33 +0100 Subject: [PATCH] update 0.7.0 --- favicon.ico | Bin 0 -> 1150 bytes index.css | 206 ++ index.htm | 627 ++++++ index.js | 1475 +++++++++++++ readme.htm | 388 ++++ sae/amiga.js | 459 ++++ sae/audio.js | 1126 ++++++++++ sae/blitter.js | 2904 +++++++++++++++++++++++++ sae/cia.js | 738 +++++++ sae/config.js | 188 ++ sae/constants.js | 317 +++ sae/copper.js | 615 ++++++ sae/cpu.js | 5232 ++++++++++++++++++++++++++++++++++++++++++++++ sae/custom.js | 350 ++++ sae/disk.js | 1674 +++++++++++++++ sae/events.js | 803 +++++++ sae/expansion.js | 177 ++ sae/input.js | 801 +++++++ sae/memory.js | 652 ++++++ sae/playfield.js | 1376 ++++++++++++ sae/rtc.js | 191 ++ sae/serial.js | 86 + sae/utils.js | 419 ++++ sae/video.js | 245 +++ 24 files changed, 21049 insertions(+) create mode 100644 favicon.ico create mode 100644 index.css create mode 100644 index.htm create mode 100644 index.js create mode 100644 readme.htm create mode 100644 sae/amiga.js create mode 100644 sae/audio.js create mode 100644 sae/blitter.js create mode 100644 sae/cia.js create mode 100644 sae/config.js create mode 100644 sae/constants.js create mode 100644 sae/copper.js create mode 100644 sae/cpu.js create mode 100644 sae/custom.js create mode 100644 sae/disk.js create mode 100644 sae/events.js create mode 100644 sae/expansion.js create mode 100644 sae/input.js create mode 100644 sae/memory.js create mode 100644 sae/playfield.js create mode 100644 sae/rtc.js create mode 100644 sae/serial.js create mode 100644 sae/utils.js create mode 100644 sae/video.js diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4b2f112efda870631f917418ad5403296a723b43 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#BdN85Tc$;^<{ePVzt4~G+RzLk` zH2+_-cY_--4&SvPui;Dv%m2DExBt4Umi%|_DgJM{YUO{w#nbl?(tq^GV&@x$JpVUk z3I9JG!|=as<>dcr=kNS?+`9e0_PJ;OYgR99z-7kCqsz2zcW50yP%80%d5+Bg2c@hZ z*UxL|a(?vxwc*B{|Md^w_^+_<#s78NmOEoJ8(B z745$6B7N!4f5%1J{%c%(_up~#QLO$rRBa--zR^v1LX|(yv2IWP6$kgT!|Vw@^=7l= zu51787w`Emx8?W$;(6;DU~yiJ`PAC3P*|?D;e{^j;=Obuw`@gQ&lK%>hC;nT6Z#zC?+XgLk zyYTVzCtTUAV0+-dmgm}!ijL?0`(^Ijh)+E|eo0P0OAJwHI5AOYaCgC bn2mpp<3DzwJP0=faU&2P0OAKg{2zh=eQ)eW literal 0 HcmV?d00001 diff --git a/index.css b/index.css new file mode 100644 index 0000000..7d623f5 --- /dev/null +++ b/index.css @@ -0,0 +1,206 @@ + +a:link { color:#2040c0; text-decoration:none; } +a:visited { color:#2040c0; text-decoration:none; } +a:hover { color:#2040c0; text-decoration:underline; } + +.alt { text-align:left; vertical-align:top; } +.alm { text-align:left; vertical-align:middle; } +.alb { text-align:left; vertical-align:bottom; } + +.act { text-align:center; vertical-align:top; } +.acm { text-align:center; vertical-align:middle; } +.acmsb { text-align:center; vertical-align:middle; font-size:x-small; font-weight:bold; } +.acb { text-align:center; vertical-align:bottom; } + +.art { text-align:right; vertical-align:top; } +.arm { text-align:right; vertical-align:middle; } +.armb { text-align:right; vertical-align:middle; font-weight:bold; } +.arms { text-align:right; vertical-align:middle; font-size:x-small; } +.armsb { text-align:right; vertical-align:middle; font-size:x-small; font-weight:bold; } +.arb { text-align:right; vertical-align:bottom; } + +.fsxs { font-size:x-small; } +.fsl { font-size:large; } +.fwb { font-weight:bold; } + +.info { + font-size:x-small; + color:gray; + vertical-align:text-top; +} + +.gray { color:gray; } +.green { color:green; } +.red { color:red; } + +body { + top:0; + left:0; + margin:0; + padding:0; + font-family:verdana; + font-size:small; + background-color:#fff; +} + +.noscript { + margin:auto; + padding:4px; + font-weight:bold; + font-size:medium; + text-align:center; + color:red; +} + +.hr { + width:100%; + border-top:1px solid #ccc; +} + +.head { + width:100%; + margin:auto; + text-align:center; + background-color:#f8f8f8; +} + +.foot { + width:100%; + margin:auto; + text-align:center; + font-size:x-small; +} + +#config { + width:800px; + margin:auto; + background-color:#fcfcfc; +} +#config_simple { + width:760px; + padding:20px; + border:1px solid #ccc; +} +.config_simple_hof { + width:50%; + margin:auto; + padding:10px; + border:1px solid #ccc; + background-color:#f8f8f8; +} +#cfg_info td { + padding:2px; +} + +#config_advanced { + width:760px; + padding:20px; + border:1px solid #ccc; + display:none; +} +#config_advanced table { + width:100%; + line-height:20px; +} + +#config .tl { + border-top:1px solid #ccc; +} +#config .bl { + border-bottom:1px solid #ccc; +} +#config .wauto { + width:auto; +} +#config select { + border:0; + border:1px solid #ccc; +} +#config .button { + width:100px; + border:1px solid #ccc; + background-color:#fff; +} +#config .button:hover { + background-color:#eee; +} +#config .sbutton { + width:100px; + padding:2px; + font-weight:bold; + border:1px solid #ccc; + background-color:#fff; +} +#config .sbutton:hover { + background-color:#eee; +} +#cfg_demo, #cfg_game { + width:100%; +} + +#emul { + display:none; +} + +#status { + width:720px; + margin:auto; + padding:2px; + text-align:center; + font-size:x-small; + color:#888; + background-color:#000; +} +#status table { + margin:auto; +} +#dskchg, #dskchg_simple { + display:none; + width:720px; + margin:auto; + padding:2px; + text-align:center; + font-size:x-small; + color:#888; + background-color:#000; +} +#dskchg, #dskchg_simple table { + margin:auto; +} + +.dbutton { + width:64px; + font-size:x-small; + color:#888; + border:1px solid #888; + background-color:#000; +} +.dbutton:hover { + color:#ccc; + border:1px solid #ccc; +} + +.readme { + width:1004px; + margin:auto; + padding:10px; + border:1px solid #ccc; + background-color:#fcfcfc; +} +.readme table { + width:100%; +} +.readme table td { + vertical-align:top; +} + +div.float_box { + overflow: hidden; + width: 100%; +} +div.float_item { + float: left; +} +div.float_clear { + clear: both; +} \ No newline at end of file diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..2b3a7be --- /dev/null +++ b/index.htm @@ -0,0 +1,627 @@ + + + + SAE - Scripted Amiga Emulator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+

Welcome!

+ This site is a demonstration of the 'SAE' project, + an experimental Amiga emulator in pure javascript and HTML5.
+ For details, please refer to the readme file.

+ Below you find a small collection of games and demos to play... +
+
+
+
+ Hall of fame +
+ + + + + + + + + + + + + + + + + +
Game + +
Demo + +
Audio
Scale 4x(1140 x 1152, press F11 for fullscreen)
+
+ + + + + + + + + + + + + + + + + + + + + +
Name
From
Year
Controls
Loading
+
+
+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CPU + + + + + +
Speed: + +
+
RAM + + + + + + + + + + + + + +
Chip: + + kb +
Slow: + + kb (pseudo-fast, bogomem) +
Fast: + + kb (real-fast, commodore a2058 in zorro2) +
+
ROM + + + + + + + + + + + + + + + + + + + +
Kickstart:unset (required)
Extended:unset (optional)
Ext. addr: + +
+
Floppy + + + + + + + + + + + + + + + + + +
DF0 + + + + + + + + +
+ + empty (optional)
+
DF1 + + + + + + + + +
+ + empty (optional)
+
DF2 + + + + + + + + +
+ + empty (optional)
+
DF3 + + + + + + + + +
+ + empty (optional)
+
+ Speed: + + ('Turbo' mode is fastest, but not very compatible) +
Audio + + Enabled + + + + + + + + + + + + + +
Mode: + +
Channels: + +
Samplerate: + + Hz +
+
Video + + Enabled +
+ + Sprite collisions + + + +
+ + Scale 4x (1440 x 1152) +
+ + NTSC +
+ + Frameskip (if the emulation is too slow, up to 4 frames will be skipped) + +
+
Keyboard + + Enabled (joysticks does always work) +
+ + Map L/R-Shift to L/R-Arrows (team17 pinball games) +
+
Port 0 + + Enabled + + + + + Move + + Fire 1 + + Fire 2 + + +
Port 1 + + Enabled + + + Move + + Fire 1 + + Fire 2 + + +
Serial + + Enabled +
+
+
+ +
+
+
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + +
pwrdf0df1df2df3fpscpu
+
+
+ + + + + +
+ +
+
+
+ + + + + + + + +
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..9a9b336 --- /dev/null +++ b/index.js @@ -0,0 +1,1475 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +*************************************************************************** +* Note: This file does not contain any emulator-code. +* It is just for the SAE-calls and some GUI-stuff... +* +**************************************************************************/ + +const db = [ + /* name company year [disks] change [en,f1,f2,map] load [keys] */ + [ + ['Agony', 'Psygnosis', '1992', + ['Agony (Disk 1).adf', + 'Agony (Disk 2).adf', + 'Agony (Disk 3).adf', + false], false, + [true, 16, 17, false], + ['Press the LMB to skip the intro.'], [['Weapon menu', 'Space']] + ], + ['Alien Breed', 'Team 17', '1991', + ['Alien Breed (Disk 1).adf', + 'Alien Breed (Disk 2).adf', + false, false], true, + [true, 16, 17, false], + ['Insert the 2nd disk manualy and HOLD the fire button.'], [['Show map', 'M']] + ], + ['Alien Breed - Tower Assault', 'Team 17', '1994', + ['Alien Breed - Tower Assault (Disk 1).adf', + 'Alien Breed - Tower Assault (Disk 2).adf', + 'Alien Breed - Tower Assault (Disk 3).adf', + 'Alien Breed - Tower Assault (Disk 4).adf'], false, + [true, 8, 17, false], + ['Press ESC to skip the intro.'], [['Show map', 'M']] + ], + ['Another World', 'Delphine - U.S. Gold', '1991', + ['Another World (Disk 1).adf', + 'Another World (Disk 2).adf', + false, false], false, + [true, 16, 17, false], [], [] + ], + ['Blood Money', 'DMA Design', '1989', + ['Blood Money (Disk 1).adf', + 'Blood Money (Disk 2).adf', + false, false], true, + [true, 16, 17, false], + ['Press the LMB until the main-nemu
and insert the 2nd disk manualy.'], [] + ], + ['Body Blows Galactic', 'Team 17', '1993', + ['Body Blows Galactic (Disk 1).adf', + 'Body Blows Galactic (Disk 2).adf', + 'Body Blows Galactic (Disk 3).adf', + false], false, + [true, 16, 17, false], ['Press the LMB to skip the intro.'], [] + ], + ['Dyna Blaster', 'Ubi Soft', '1992', + ['Dyna Blaster.adf', + false, false, false], false, + [true, 16, 17, false], ['Press the LMB to skip the intro.'], [] + ], + ['Lotus Turbo Challenge 2', 'Gremlin', '1991', + ['Lotus Turbo Challenge 2.adf', + false, false, false], false, + [true, 16, 17, false], ['Press the LMB to skip the intro.'], [] + ], + ['Pinball Dreams', 'DICE', '1992', + ['Pinball Dreams (Disk 1).adf', + 'Pinball Dreams (Disk 2).adf', + false, false], false, + [false, 0, 0, true], [], [['Select table','F1-F4'],['Start game','F1-F8'],['Shoot Ball','Down-Arrow'],['Flip','L/R-Arrows'],['Shake table','Space'],['Change table','ESC and then y/z'],['En/Disable music','M']] + ], + /*['Pinball Fantasies', 'DICE', '1992', + ['Pinball Fantasies (Disk 1).adf', + 'Pinball Fantasies (Disk 3).adf', + false, false], true, + [false, 0, 0, true], + ['When asked to remove write protection from disk, press the LMB.'], + [['Select table','F1-F4'],['Start game','F1-F8'],['Shoot Ball','Down-Arrow'],['Flip','L/R-Arrows'],['Shake table','Space'],['Change table','ESC and then y/z'],['En/Disable music','M']] + ],*/ + ['Prince of Persia', 'Domark', '1991', + ['Prince of Persia.adf', + false, false, false], false, + [true, 16, 17, false], ['Press the LMB to skip the intro.'], [] + ], + ['Project-X - Special Edition 93', 'Team 17', '1993', + ['Project-X - Special Edition 93 (Disk 1).adf', + 'Project-X - Special Edition 93 (Disk 2).adf', + 'Project-X - Special Edition 93 (Disk 3).adf', + false], false, + [true, 16, 17, false], + ['Press the LMB to skip the intro.'], [['Select weapon', 'Space (not in trainer mode)']] + ], + ['Rick Dangerous', 'Firebird', '1989', + ['Rick Dangerous.adf', + false, false, false], false, + [true, 16, 17, false], [], [] + ], + ['Shadow of the Beast II', 'Reflections', '1990', + ['Shadow of the Beast II (Disk 1).adf', + 'Shadow of the Beast II (Disk 2).adf', + 'Shadow of the Beast II (Disk 3).adf', + false], true, + [true, 16, 17, false], + ['Change the disks manualy when asked.'], [] + ], + ['Superfrog', 'Team 17', '1993', + ['Superfrog (Disk 1).adf', + 'Superfrog (Disk 2).adf', + 'Superfrog (Disk 3).adf', + 'Superfrog (Disk 4).adf'], true, + [true, 16, 17, false], + ['Change the disks manualy when asked.'], [] + ], + ['Terminator 2 - Judgment Day', 'Ocean', '1991', + ['Terminator 2 - Judgment Day (Disk 1).adf', + 'Terminator 2 - Judgment Day (Disk 2).adf', + false, false], true, + [true, 16, 17, false], + ['Press the LMB to skip the intro
and insert the 2nd disk manualy.'], [] + ], + ['Turrican II - The Final Fight', 'Rainbow Arts', '1991', + ['Turrican II - The Final Fight (Disk 1).adf', + 'Turrican II - The Final Fight (Disk 2).adf', + false, false], false, + [true, 16, 17, false], [], [['Fire-beam','Hold Fire 1'],['Roll on floor', 'Down-arrow + Fire 1'],['Bomb', 'Fire 2'],['Super-Bomb', 'Fire 1 + Fire 2']] + ], + ['Wings of Death', 'Thalion', '1990', + ['Wings of Death (Disk 1).adf', + 'Wings of Death (Disk 2).adf', + false, false], true, + [true, 16, 17, false], + ['Press the LMB until the main-nemu
and insert the 2nd disk manualy.'], [] + ], + ], + [ + ['242', 'Virtual Dreams', '1992', + ['242.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['9 Fingers', 'Spaceballs', '1993', + ['9 Fingers (Disk 1).adf', + '9 Fingers (Disk 2).adf',false,false], false, + [true, 16, 17, false], [], [] + ], + ['Alpha and Omega', 'Pure Metal Coders', '1991', + ['Alpha and Omega.adf', + false,false,false], true, + [true, 16, 17, false], [], [] + ], + ['Copper Master', 'Angels', '1990', + ['Copper Master.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Deja Vu', 'Anarchy', '1992', + ['Deja Vu.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Elysium', 'Sanity', '1991', + ['Elysium.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Ecliptica', 'TRSI', '1991', + ['Ecliptica.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Enigma', 'Phenomena', '1991', + ['Enigma.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Global Trash', 'Silents', '1992', + ['Global Trash.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Hardwired', 'Crionics, Silents', '1992', + ['Hardwired (Disk 1).adf', + 'Hardwired (Disk 2).adf', + false,false], true, + [true, 16, 17, false], + ['Insert the 2nd disk manualy,
click RMB when done.'], [] + ], + ['Ice', 'Silents', '1991', + ['Ice.adf',false,false,false], false, + [true, 16, 17, false], ['Press LMB at the intro-screen'], [] + ], + ['Lost World', 'Balance DK', '1992', + ['Lost World.adf',false,false,false], false, + [true, 16, 17, false], ['Press LMB at the intro-screen'], [] + ], + ['Mental Hangover', 'Scoopex', '1992', + ['Mental Hangover.adf',false,false,false], false, + [true, 16, 17, false], ['Press LMB at the intro-screen'], [] + ], + ['Multica', 'Andromeda', '1992', + ['Multica.adf',false,false,false], false, + [true, 16, 17, false], ['Press LMB at the intro-screen'], [] + ], + ['Project-X (demo rolling)', 'Team 17', '1992', + ['Project-X (demo-rolling).adf',false,false,false], false, + [true, 16, 17, false], [], [['Skip level','Fire']] + ], + ['Rampage', 'TEK', '1994', + ['Rampage.adf',false,false,false], false, + [true, 16, 17, false], ['Press LMB at the intro-screen'], [] + ], + ['State of the Art', 'Spaceballs', '1992', + ['State of the Art.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Static Chaos', 'Silents', '1992', + ['Static Chaos.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Total Destruction', 'Crionics', '1990', + ['Total Destruction.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['Wayfarer', 'Spaceballs', '1992', + ['Wayfarer.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ['World of Commodore', 'Sanity', '1992', + ['World of Commodore.adf',false,false,false], false, + [true, 16, 17, false], [], [] + ], + ] +]; +var dbGrp = 0; +var dbNum = 0; + +const aros_rom_file = 'aros-amiga-m68k-rom.bin'; +const aros_rom_url = 'http://'+window.location.hostname+'/db/'+aros_rom_file; +const aros_rom_size = 0x80000; +const aros_rom_crc = 0xea48b4d1; //0xfc4635e1; +const aros_ext_file = 'aros-amiga-m68k-ext.bin'; +const aros_ext_url = 'http://'+window.location.hostname+'/db/'+aros_ext_file; +const aros_ext_size = 0x80000; +const aros_ext_crc = 0x60871435; //0xc612f82e; + +var mode = 0; +var paused = false; +var dskchg = false; +var dskchgList = []; + +var cache = null; +var config = null; + +/*-----------------------------------------------------------------------*/ + +function dechex(dec) { return dec.toString(16); } + +function Cache() { + var roms = [null,null]; + var disks = []; + + this.loadRom = function(num) { + if (roms[num]) { + console.log('loadRom.loadRom() %d is cached', num); + return roms[num]; + } + console.log('loadRom.loadRom() downloading %d', num); + + var url, size, crc; + switch (num) { + case 0: + url = aros_rom_url; + size = aros_rom_size; + crc = aros_rom_crc; + break; + case 1: + url = aros_ext_url; + size = aros_ext_size; + crc = aros_ext_crc; + break; + } + var data = loadRemoteSync(url); + if (typeof(data) == 'number') { + alert('Can\'t download '+url+' (http status: '+data+')'); + } else { + if (data.length == size) { + //console.log(dechex(crc32(data))); + if (crc32(data) == crc) { + roms[num] = data; + return data; + } else + alert('Wrong checksum for '+url+' (is $'+dechex(crc32(data))+', should $'+dechex(crc)+')'); + } else + alert('Wrong file-length for '+url+' ('+size+')'); + } + return null; + } + + this.loadDisk = function(url) { + for (var i = 0; i < disks.length; i++) { + if (disks[i][0] == url) { + console.log('Cache.loadDisk() %s is cached', url); + return disks[i][1]; + } + } + console.log('Cache.loadDisk() downloading %s', url); + + var size = 0xdc000, crc = false; + var data = loadRemoteSync(url); + if (typeof(data) == 'number') { + alert('Can\'t download '+url+' (http status: '+data+')'); + } else { + if (data.length == size) { + if (crc === false || crc32(data) == crc) { + disks.push([url, data]); + return data; + } else + alert('Wrong checksum for '+url+' (is $'+dechex(crc32(data))+', should $'+dechex(crc)+')'); + } else + alert('Wrong file-length for '+url+' ('+size+')'); + } + return null; + } +} + +/*-----------------------------------------------------------------------*/ +/* utils */ + +function dump(obj) { + var out = ''; + if (obj) { + for (var i in obj) { + out += i + ': ' + obj[i] + '\n'; + } + } + alert(out); + /*var pre = document.createElement('pre'); + pre.innerHTML = out; + document.body.appendChild(pre);*/ +} + +function crc32(str, crc) { + + const tab = + '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 '+ + '0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 '+ + '1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 '+ + '136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 '+ + '3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B '+ + '35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 '+ + '26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F '+ + '2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D '+ + '76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 '+ + '7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 '+ + '6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 '+ + '65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 '+ + '4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB '+ + '4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 '+ + '5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F '+ + '5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD '+ + 'EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 '+ + 'E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 '+ + 'F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 '+ + 'FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 '+ + 'D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B '+ + 'D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 '+ + 'CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F '+ + 'C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D '+ + '9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 '+ + '95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 '+ + '86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 '+ + '88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 '+ + 'A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB '+ + 'AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 '+ + 'BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF '+ + 'B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'; + + if (crc == window.undefined) crc = 0; + + crc = crc ^ (-1); + for (var i = 0, len = str.length; i < len; i++) + crc = (crc >>> 8) ^ parseInt(tab.substr(((crc ^ str.charCodeAt(i)) & 0xff) * 9, 8), 16); + crc = crc ^ (-1); + + return crc < 0 ? crc + 0x100000000 : crc; +} + +function openNewTab(url) { + window.open(url, '_blank'); + window.focus(); +} + +function getSelectValue(e) { + for (var i = 0; i < e.length; i++) { + if (e[i].selected) return e[i].value; + } + return false; +} +function unselect(e) { + for (var i = 0; i < e.length; i++) { + if (e[i].selected) { + e[i].selected = false; + break; + } + } +} + +function styleDisplayBlock(id, show) { + var e = document.getElementById(id); + e.style.display = show ? 'block' : 'none'; +} +function styleDisplayInline(id, show) { + var e = document.getElementById(id); + e.style.display = show ? 'inline' : 'none'; +} +function styleDisplayTable(id, show) { + var e = document.getElementById(id); + e.style.display = show ? 'table' : 'none'; +} +function styleDisplayTableRow(id, show) { + var e = document.getElementById(id); + e.style.display = show ? 'table-row' : 'none'; +} +function disabled(id, d) { + document.getElementById(id).disabled = d ? 'disabled' : ''; +} + +function toggleFullScreen() { + if ((document.fullScreenElement && document.fullScreenElement !== null) || // alternative standard method + (!document.mozFullScreenElement && !document.webkitFullScreenElement)) { // current working methods + if (document.documentElement.requestFullScreen) { + document.documentElement.requestFullScreen(); + } else if (document.documentElement.mozRequestFullScreen) { + document.documentElement.mozRequestFullScreen(); + } else if (document.documentElement.webkitRequestFullScreen) { + document.documentElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); + } + } else { + if (document.cancelFullScreen) { + document.cancelFullScreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitCancelFullScreen) { + document.webkitCancelFullScreen(); + } + } +} + +function loadLocalId(id, callback) { + var e = document.getElementById(id).files[0]; + var reader = new FileReader(); + reader.onload = callback; + reader.readAsBinaryString(e); +} +function loadLocal(e, callback) { + var reader = new FileReader(); + reader.onload = callback; + reader.readAsBinaryString(e); +} + +function loadRemote(url, size, crc, callback) { + var req = new XMLHttpRequest(); + req.open('GET', url, true); + req.overrideMimeType('text\/plain; charset=x-user-defined'); + req.onreadystatechange = function(e) { + if (req.readyState == 4) { + if (req.status == 200) { + if (req.responseText.length == size) { + if (crc === false || crc32(req.responseText) == crc) + callback(0, req.responseText); + else + callback(1, crc32(req.responseText)); + } else + callback(2, req.responseText.length); + } else + callback(3, req.status); + } + } + req.send(null); +} +function loadRemoteSync(url) { + var req = new XMLHttpRequest(); + req.open('GET', url, false); + req.overrideMimeType('text\/plain; charset=x-user-defined'); + req.send(null); + return req.status == 200 ? req.responseText : parseInt(req.status); +} + +/*-----------------------------------------------------------------------*/ +/* simple config */ + +function setSimpleConfig() { + var s = document.getElementById('cfg_game'); + if (s.length == 1) { + for (var i = 0; i < db[0].length; i++) { + var e = document.createElement('option'); + e.value = String(1 + i); + e.text = db[0][i][0]; + s.add(e, null); + } + } + s = document.getElementById('cfg_demo'); + if (s.length == 1) { + for (var i = 0; i < db[1].length; i++) { + var e = document.createElement('option'); + e.value = String(1 + i); + e.text = db[1][i][0]; + s.add(e, null); + } + } + styleDisplayBlock('config_simple', 1); + document.getElementById('cfg_audio_enabled_1').checked = true; + document.getElementById('cfg_video_scale_1').checked = false; + unselect(document.getElementById('cfg_demo')); + unselect(document.getElementById('cfg_game')); + styleDisplayTable('cfg_info', 0); + + styleDisplayInline('dskchg_grp', 0); +} + +function getSimpleFloppy() { + //console.log('loadDisks() %d %d', dbGrp, dbNum); + + config.floppy.speed = 1000; /* 1000 percent == x10 */ + + if (dbNum == 0) { + config.floppy.drive[0].type = SAEV_Config_Floppy_Type_35_DD; + config.floppy.drive[0].name = null; + config.floppy.drive[0].data = null; + config.floppy.drive[1].type = SAEV_Config_Floppy_Type_35_DD; + config.floppy.drive[1].name = null; + config.floppy.drive[1].data = null; + config.floppy.drive[2].type = SAEV_Config_Floppy_Type_None; + config.floppy.drive[2].name = null; + config.floppy.drive[2].data = null; + config.floppy.drive[3].type = SAEV_Config_Floppy_Type_None; + config.floppy.drive[3].name = null; + config.floppy.drive[3].data = null; + return true; + } + + if (db[dbGrp - 1][dbNum - 1] == window.undefined) { + //alert('bug!'); + return false; + } + var item = db[dbGrp - 1][dbNum - 1]; + var baseUrl = 'http://'+window.location.hostname+'/db/'; + if (dbGrp == 1) baseUrl += 'games/'; + else if (dbGrp == 2) baseUrl += 'demos/'; + + + dskchgList = []; + if (item[4]) { + var i, filename, url; + + for (i = 0; i < 4; i++) { + filename = item[3][i]; + if (filename !== false) { + filename = filename.substr(0, filename.search('.adf')); + dskchgList.push(filename); + } + } + filename = item[3][0]; + url = baseUrl + filename; + if ((config.floppy.drive[0].data = cache.loadDisk(url)) !== null) { + config.floppy.drive[0].type = SAEV_Config_Floppy_Type_35_DD; + config.floppy.drive[0].name = filename; + } else + return false; + + for (i = 1; i < 4; i++) { + filename = item[3][i]; + if (filename !== false) { + url = baseUrl + filename; + if (cache.loadDisk(url) === null) + return false; + } + config.floppy.drive[i].type = SAEV_Config_Floppy_Type_None; + config.floppy.drive[i].name = null; + config.floppy.drive[i].data = null; + } + } else { + for (var i = 0; i < 4; i++) { + var filename = item[3][i]; + if (filename !== false) { + var url = baseUrl + filename; + if ((config.floppy.drive[i].data = cache.loadDisk(url)) !== null) { + config.floppy.drive[i].type = SAEV_Config_Floppy_Type_35_DD; + config.floppy.drive[i].name = filename; + } else + return false; + } else { + config.floppy.drive[i].type = SAEV_Config_Floppy_Type_None; + config.floppy.drive[i].name = null; + config.floppy.drive[i].data = null; + } + } + } + return true; +} +function getSimpleConfig() { + var item = dbNum > 0 ? db[dbGrp - 1][dbNum - 1] : null; + var e, num; + + config.cpu.speed = SAEV_Config_CPU_Speed_Original; + + //config.chipset.type = SAEV_Config_Chipset_Type_OCS; + config.chipset.colLevel = SAEV_Config_Chipset_ColLevel_None; + + config.ram.chip.size = SAEV_Config_RAM_Chip_Size_512K; + config.ram.slow.size = SAEV_Config_RAM_Slow_Size_512K; + config.ram.fast.size = SAEV_Config_RAM_Fast_Size_512K; + + config.rom.name = aros_rom_file; + config.rom.size = SAEV_Config_ROM_Size_512K; + if ((config.rom.data = cache.loadRom(0)) === null) + return false; + + config.ext.name = aros_ext_file; + config.ext.size = SAEV_Config_EXT_Size_512K; + config.ext.addr = SAEV_Config_EXT_Addr_E0; + if ((config.ext.data = cache.loadRom(1)) === null) + return false; + + if (!getSimpleFloppy()) + return false; + + e = document.getElementById('cfg_audio_enabled_1'); + config.audio.enabled = e.checked ? true : false; + if (config.audio.enabled) { + config.audio.mode = SAEV_Config_Audio_Mode_Play_Best; + config.audio.channels = SAEV_Config_Audio_Channels_Stereo; + //config.audio.rate = SAEV_Config_Audio_Rate_48000; + config.audio.rate = SAEV_Config_Audio_Rate_44100; + } + + config.video.enabled = true; + e = document.getElementById('cfg_video_scale_1'); + config.video.scale = e.checked ? true : false; + config.video.ntsc = false; + config.video.skip = true; + config.video.id = 'myVideo'; + + config.keyboard.enabled = true; + config.keyboard.mapShift = (item !== null && item[5][3]) ? true : false; + + config.ports[0].type = SAEV_Config_Ports_Type_Mouse; + /*config.ports[0].type = SAEV_Config_Ports_Type_Joy0; + config.ports[0].move = ; + config.ports[0].fire[0] = ; + config.ports[0].fire[1] = ;*/ + if (item !== null && item[5][0]) { + config.ports[1].type = SAEV_Config_Ports_Type_Joy1; + config.ports[1].move = SAEV_Config_Ports_Move_Arrows; + config.ports[1].fire[0] = item[5][1]; + config.ports[1].fire[1] = item[5][2]; + } else { + config.ports[1].type = SAEV_Config_Ports_Type_None; + config.ports[1].move = SAEV_Config_Ports_Move_None; + config.ports[1].fire[0] = SAEV_Config_Ports_Fire_None; + config.ports[1].fire[1] = SAEV_Config_Ports_Fire_None; + } + config.serial.enabled = false; + + config.hooks.error = hooks_error; + config.hooks.power_led = hooks_power_led; + config.hooks.floppy_motor = hooks_floppy_motor; + config.hooks.floppy_step = hooks_floppy_step; + config.hooks.fps = hooks_fps; + config.hooks.cpu = hooks_cpu; + return true; +} + +/*-----------------------------------------------------------------------*/ +/* advanced config */ + +function setRomName(name) { + document.getElementById('cfg_rom_name').className = name === null ? 'red' : ''; + document.getElementById('cfg_rom_name').innerHTML = name === null ? 'unset (required)' : name; +} +function setExtName(name) { + document.getElementById('cfg_ext_name').className = name === null ? 'gray' : ''; + document.getElementById('cfg_ext_name').innerHTML = name === null ? 'unset (optional)' : name; +} +function setFloppyName(n, name) { + document.getElementById('cfg_df'+n+'_name').className = name === null ? 'gray' : ''; + document.getElementById('cfg_df'+n+'_name').innerHTML = name === null ? 'unset (optional)' : name; +} + +function setFireButton(id, fire) { + var e = document.getElementById(id); + switch (fire) { + case 0: e[0].selected = true; break; + case 16: e[1].selected = true; break; + case 17: e[2].selected = true; break; + case 13: e[3].selected = true; break; + case 32: e[4].selected = true; break; + case 8: e[5].selected = true; break; + case 96: e[6].selected = true; break; + case 106: e[7].selected = true; break; + case 107: e[8].selected = true; break; + case 109: e[9].selected = true; break; + case 110: e[10].selected = true; break; + case 111: e[11].selected = true; break; + case 46: e[12].selected = true; break; + case 45: e[13].selected = true; break; + case 34: e[14].selected = true; break; + case 33: e[15].selected = true; break; + case 35: e[16].selected = true; break; + case 36: e[17].selected = true; break; + case 19: e[18].selected = true; break; + case 144: e[19].selected = true; break; + case 145: e[20].selected = true; break; + case 49: e[21].selected = true; break; + case 50: e[22].selected = true; break; + } +} + +function fireButtonName(fire) { + switch (fire) { + case 0: return 'None'; + case 16: return 'Shift'; + case 17: return 'Ctrl'; + case 13: return 'Enter'; + case 32: return 'Space'; + case 8: return 'Backspace'; + case 96: return 'Numpad 0'; + case 106: return 'Numpad *'; + case 107: return 'Numpad '; + case 109: return 'Numpad -'; + case 110: return 'Numpad .'; + case 111: return 'Numpad /'; + case 46: return 'Delete'; + case 45: return 'Insert'; + case 34: return 'Page down'; + case 33: return 'Page up'; + case 35: return 'End'; + case 36: return 'Home'; + case 19: return 'Pause'; + case 144: return 'Num lock'; + case 145: return 'Scroll lock'; + case 49: return '1'; + case 50: return '2'; + } +} + +function setFloppy(n) { + if (config.floppy.drive[n].type != SAEV_Config_Floppy_Type_None) { + document.getElementById('cfg_df'+n+'_enabled').checked = true; + switch (config.floppy.drive[n].type) { + case SAEV_Config_Floppy_Type_35_DD: document.getElementById('cfg_df'+n+'_type')[0].selected = true; break; + case SAEV_Config_Floppy_Type_35_HD: document.getElementById('cfg_df'+n+'_type')[1].selected = true; break; + case SAEV_Config_Floppy_Type_525_SD: document.getElementById('cfg_df'+n+'_type')[2].selected = true; break; + } + if (config.floppy.drive[n].name) { + setFloppyName(n, config.floppy.drive[n].name); + styleDisplayInline('cfg_df'+n+'_eject', 1); + } else { + setFloppyName(n, null); + styleDisplayInline('cfg_df'+n+'_eject', 0); + } + styleDisplayInline('cfg_df'+n+'_grp', 1); + } else { + document.getElementById('cfg_df'+n+'_enabled').checked = false; + styleDisplayInline('cfg_df'+n+'_grp', 0); + } + switch (config.floppy.speed) { + case SAEV_Config_Floppy_Speed_Turbo: document.getElementById('cfg_floppy_speed')[0].selected = true; break; + case SAEV_Config_Floppy_Speed_Original: document.getElementById('cfg_floppy_speed')[1].selected = true; break; + case 200: document.getElementById('cfg_floppy_speed')[2].selected = true; break; + case 500: document.getElementById('cfg_floppy_speed')[3].selected = true; break; + case 1000: document.getElementById('cfg_floppy_speed')[4].selected = true; break; + } +} + +function setConfig() { + var e = document.getElementById('cfg_cpu_speed'); + switch (config.cpu.speed) { + case SAEV_Config_CPU_Speed_Original: e[0].selected = true; break; + case SAEV_Config_CPU_Speed_Maximum: e[1].selected = true; break; + } + + var e = document.getElementById('cfg_mem_chip'); + switch (config.ram.chip.size) { + case SAEV_Config_RAM_Chip_Size_256K: e[0].selected = true; break; + case SAEV_Config_RAM_Chip_Size_512K: e[1].selected = true; break; + case SAEV_Config_RAM_Chip_Size_1M: e[2].selected = true; break; + case SAEV_Config_RAM_Chip_Size_2M: e[3].selected = true; break; + } + e = document.getElementById('cfg_mem_slow'); + switch (config.ram.slow.size) { + case SAEV_Config_RAM_Slow_Size_None: e[0].selected = true; break; + case SAEV_Config_RAM_Slow_Size_256K: e[1].selected = true; break; + case SAEV_Config_RAM_Slow_Size_512K: e[2].selected = true; break; + case SAEV_Config_RAM_Slow_Size_1M: e[3].selected = true; break; + case SAEV_Config_RAM_Slow_Size_1536K: e[4].selected = true; break; + } + e = document.getElementById('cfg_mem_fast'); + switch (config.ram.fast.size) { + case SAEV_Config_RAM_Fast_Size_None: e[0].selected = true; break; + case SAEV_Config_RAM_Fast_Size_512K: e[1].selected = true; break; + case SAEV_Config_RAM_Fast_Size_1M: e[2].selected = true; break; + case SAEV_Config_RAM_Fast_Size_2M: e[3].selected = true; break; + case SAEV_Config_RAM_Fast_Size_4M: e[4].selected = true; break; + case SAEV_Config_RAM_Fast_Size_8M: e[5].selected = true; break; + } + + setRomName(config.rom.size ? config.rom.name : null); + + if (config.ext.size) { + setExtName(config.ext.name); + styleDisplayInline('cfg_ext_remove', 1); + switch (config.ext.addr) { + case SAEV_Config_EXT_Addr_E0: document.getElementById('cfg_ext_addr')[0].selected = true; break; + case SAEV_Config_EXT_Addr_F0: document.getElementById('cfg_ext_addr')[1].selected = true; break; + } + styleDisplayTableRow('cfg_ext_addr_grp', 1); + } else { + setExtName(null); + styleDisplayInline('cfg_ext_remove', 0); + styleDisplayTableRow('cfg_ext_addr_grp', 0); + } + + for (var i = 0; i < 4; i++) + setFloppy(i); + + document.getElementById('cfg_audio_enabled').checked = config.audio.enabled; + switch (config.audio.mode) { + case SAEV_Config_Audio_Mode_Emul: document.getElementById('cfg_audio_mode')[0].selected = true; break; + case SAEV_Config_Audio_Mode_Play: document.getElementById('cfg_audio_mode')[1].selected = true; break; + case SAEV_Config_Audio_Mode_Play_Best: document.getElementById('cfg_audio_mode')[2].selected = true; break; + } + switch (config.audio.channels) { + case SAEV_Config_Audio_Channels_Mono: document.getElementById('cfg_audio_channels')[0].selected = true; break; + case SAEV_Config_Audio_Channels_Stereo: document.getElementById('cfg_audio_channels')[1].selected = true; break; + } + switch (config.audio.rate) { + case SAEV_Config_Audio_Rate_11025: document.getElementById('cfg_audio_rate')[0].selected = true; break; + case SAEV_Config_Audio_Rate_22050: document.getElementById('cfg_audio_rate')[1].selected = true; break; + case SAEV_Config_Audio_Rate_44100: document.getElementById('cfg_audio_rate')[2].selected = true; break; + case SAEV_Config_Audio_Rate_48000: document.getElementById('cfg_audio_rate')[3].selected = true; break; + } + styleDisplayTable('cfg_audio_grp', config.audio.enabled); + + document.getElementById('cfg_video_enabled').checked = config.video.enabled != 0; + document.getElementById('cfg_chipset_cl_enabled').checked = config.chipset.colLevel != SAEV_Config_Chipset_ColLevel_None; + switch (config.chipset.colLevel) { + case SAEV_Config_Chipset_ColLevel_Sprite_Sprite: document.getElementById('cfg_chipset_cl')[0].selected = true; break; + case SAEV_Config_Chipset_ColLevel_Sprite_Playfield: document.getElementById('cfg_chipset_cl')[1].selected = true; break; + } + styleDisplayInline('cfg_chipset_cl_grp', config.chipset.colLevel != SAEV_Config_Chipset_ColLevel_None); + document.getElementById('cfg_video_scale').checked = config.video.scale; + document.getElementById('cfg_video_ntsc').checked = config.video.ntsc != 0; + document.getElementById('cfg_video_skip').checked = config.video.skip != 0; + styleDisplayBlock('cfg_video_grp', config.video.enabled != 0); + + document.getElementById('cfg_keyborad_enabled').checked = config.keyboard.enabled != 0; + document.getElementById('cfg_keyborad_mapshift').checked = config.keyboard.mapShift != 0; + styleDisplayBlock('cfg_keyborad_grp', config.keyboard.enabled != 0); + + document.getElementById('cfg_ports_0_enabled').checked = config.ports[0].type != SAEV_Config_Ports_Type_None; + e = document.getElementById('cfg_ports_0'); + switch (config.ports[0].type) { + case SAEV_Config_Ports_Type_Mouse: document.getElementById('cfg_ports_0')[0].selected = true; break; + case SAEV_Config_Ports_Type_Joy0: document.getElementById('cfg_ports_0')[1].selected = true; break; + } + switch (config.ports[0].move) { + case SAEV_Config_Ports_Move_Arrows: document.getElementById('cfg_ports_0_move')[0].selected = true; break; + case SAEV_Config_Ports_Move_Numpad: document.getElementById('cfg_ports_0_move')[1].selected = true; break; + case SAEV_Config_Ports_Move_WASD: document.getElementById('cfg_ports_0_move')[2].selected = true; break; + } + setFireButton('cfg_ports_0_fire_1', config.ports[0].fire[0]); + setFireButton('cfg_ports_0_fire_2', config.ports[0].fire[1]); + styleDisplayInline('cfg_ports_0_grp', config.ports[0].type != SAEV_Config_Ports_Type_None); + styleDisplayInline('cfg_ports_0_grp2', config.ports[0].type == SAEV_Config_Ports_Type_Joy0); + + e = document.getElementById('cfg_ports_1_enabled').checked = config.ports[1].type != SAEV_Config_Ports_Type_None; + e = document.getElementById('cfg_ports_1'); + switch (config.ports[1].type) { + case SAEV_Config_Ports_Type_Joy1: document.getElementById('cfg_ports_1')[0].selected = true; break; + } + switch (config.ports[1].move) { + case SAEV_Config_Ports_Move_Arrows: document.getElementById('cfg_ports_1_move')[0].selected = true; break; + case SAEV_Config_Ports_Move_Numpad: document.getElementById('cfg_ports_1_move')[1].selected = true; break; + case SAEV_Config_Ports_Move_WASD: document.getElementById('cfg_ports_1_move')[2].selected = true; break; + } + setFireButton('cfg_ports_1_fire_1', config.ports[1].fire[0]); + setFireButton('cfg_ports_1_fire_2', config.ports[1].fire[1]); + styleDisplayInline('cfg_ports_1_grp', config.ports[1].type == SAEV_Config_Ports_Type_Joy1); + + e = document.getElementById('cfg_serial_enabled').checked = config.serial.enabled != 0; + + styleDisplayInline('dskchg_grp', 1); +} + +function getConfig() { + var e; + + e = document.getElementById('cfg_cpu_speed'); + config.cpu.speed = parseInt(getSelectValue(e)); + + //e = document.getElementById('cfg_chipset_type'); + //config.chipset.type = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_chipset_cl_enabled'); + if (e.checked) { + e = document.getElementById('cfg_chipset_cl'); + config.chipset.colLevel = parseInt(getSelectValue(e)); + } else + config.chipset.colLevel = SAEV_Config_Chipset_ColLevel_None; + + e = document.getElementById('cfg_mem_chip'); + config.ram.chip.size = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_mem_slow'); + config.ram.slow.size = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_mem_fast'); + config.ram.fast.size = parseInt(getSelectValue(e)); + + if (!config.rom.name) { + alert('No Kickstart ROM.'); + return false; + } + + e = document.getElementById('cfg_floppy_speed'); + config.floppy.speed = parseInt(getSelectValue(e)); + + e = document.getElementById('cfg_audio_enabled'); + config.audio.enabled = e.checked ? true : false; + if (config.audio.enabled) { + e = document.getElementById('cfg_audio_mode'); + config.audio.mode = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_audio_channels'); + config.audio.channels = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_audio_rate'); + config.audio.rate = parseInt(getSelectValue(e)); + } + + e = document.getElementById('cfg_video_enabled'); + config.video.enabled = e.checked ? true : false; + e = document.getElementById('cfg_video_scale'); + config.video.scale = e.checked ? true : false; + e = document.getElementById('cfg_video_ntsc'); + config.video.ntsc = e.checked ? true : false; + e = document.getElementById('cfg_video_skip'); + config.video.skip = e.checked ? true : false; + config.video.id = 'myVideo'; + + e = document.getElementById('cfg_keyborad_enabled'); + config.keyboard.enabled = e.checked ? true : false; + e = document.getElementById('cfg_keyborad_mapshift'); + config.keyboard.mapShift = e.checked ? true : false; + + e = document.getElementById('cfg_ports_0'); + config.ports[0].type = parseInt(getSelectValue(e)); + if (config.ports[0].type == SAEV_Config_Ports_Type_Joy0) { + e = document.getElementById('cfg_ports_0_move'); + config.ports[0].move = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_ports_0_fire_1'); + config.ports[0].fire[0] = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_ports_0_fire_2'); + config.ports[0].fire[1] = parseInt(getSelectValue(e)); + if (config.ports[0].fire[0] != SAEV_Config_Ports_Fire_None && config.ports[0].fire[0] == config.ports[0].fire[1]) { + alert('Fire-button 1/2 on port 0 can\'t be the same.'); + return false; + } + } + e = document.getElementById('cfg_ports_1'); + config.ports[1].type = parseInt(getSelectValue(e)); + if (config.ports[1].type == SAEV_Config_Ports_Type_Joy1) { + e = document.getElementById('cfg_ports_1_move'); + config.ports[1].move = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_ports_1_fire_1'); + config.ports[1].fire[0] = parseInt(getSelectValue(e)); + e = document.getElementById('cfg_ports_1_fire_2'); + config.ports[1].fire[1] = parseInt(getSelectValue(e)); + if (config.ports[1].fire[0] != SAEV_Config_Ports_Fire_None && config.ports[1].fire[0] == config.ports[1].fire[1]) { + alert('Fire-button 1/2 on port 1 can\'t be the same.'); + return false; + } + } + + e = document.getElementById('cfg_serial_enabled'); + config.serial.enabled = e.checked ? true : false; + + config.hooks.error = hooks_error; + config.hooks.power_led = hooks_power_led; + config.hooks.floppy_motor = hooks_floppy_motor; + config.hooks.floppy_step = hooks_floppy_step; + config.hooks.fps = hooks_fps; + config.hooks.cpu = hooks_cpu; + return true; +} + +/*-----------------------------------------------------------------------*/ +/* main */ + +function init() { + cache = new Cache(); + + config = SAE({cmd:'init'}); + //config = SAE({cmd:'getConfig'}); + //console.log(config); + setSimpleConfig(); +} + +function start() { + document.body.style.backgroundColor = '#000'; + styleDisplayBlock('base', 0); + styleDisplayBlock('emul', 1); + + //SAE({cmd:'setConfig',data:config}); + /*var result = SAE({cmd:'start'}); + if (result.error != SAEE_None) { + stop(); + alert(result.message); + }*/ + SAE({cmd:'start'}); +} + +function simpleStart2() { + if (getSimpleConfig()) + start(); + else { + disabled('cfg_simple_start', 0); + document.getElementById('cfg_simple_start').innerHTML = 'Play'; + } +} +function simpleStart() { + disabled('cfg_simple_start', 1); + document.getElementById('cfg_simple_start').innerHTML = 'Loading...'; + setTimeout('simpleStart2()', 1); +} + +function advandedStart2() { + if (getConfig()) + start(); + else { + disabled('cfg_start', 0); + document.getElementById('cfg_start').innerHTML = 'Play'; + } +} +function advandedStart() { + disabled('cfg_start', 1); + document.getElementById('cfg_start').innerHTML = 'Loading...'; + setTimeout('advandedStart2()', 1); +} + +function stop() { + SAE({cmd:'stop'}); + + if (paused) { + var e = document.getElementById('status_pr'); + e.value = 'pause'; + e.onclick = function() { pause(1); } + paused = false; + } + if (dskchg) + dskchgClose(); + + disabled('cfg_simple_start', 0); + disabled('cfg_start', 0); + document.getElementById('cfg_simple_start').innerHTML = 'Play'; + document.getElementById('cfg_start').innerHTML = 'Play'; + + if (mode == 1) + setConfig(); + + styleDisplayBlock('emul', 0); + styleDisplayBlock('base', 1); + document.body.style.backgroundColor = '#fff'; +} + +function reset(p) { + SAE({cmd:'reset'}); +} + +function pause(p) { + var e = document.getElementById('status_pr'); + e.innerHTML = p ? 'Resume' : 'Pause'; + e.onclick = function() { pause(1 - p); } + + paused = p; + + SAE({cmd:'pause',state:p}); +} + +/*-----------------------------------------------------------------------*/ +/* config */ + +function switchCfg(m) +{ + styleDisplayBlock('config_advanced', m == 1); + styleDisplayBlock('config_simple', m != 1); + if (m == 0) + setSimpleConfig(); + else + setConfig(); + + mode = m; +} + +/*-----------------------------------------------------------------------*/ +/* simple config */ + +function preSelect(grp) +{ + var num; + if (grp == 1) { + num = parseInt(getSelectValue(document.getElementById('cfg_game'))); + unselect(document.getElementById('cfg_demo')); + } else { + unselect(document.getElementById('cfg_game')); + num = parseInt(getSelectValue(document.getElementById('cfg_demo'))); + } + dbGrp = grp; + dbNum = num; + + if (num == 0) { + styleDisplayTable('cfg_info', 0); + return; + } + var item = db[grp - 1][num - 1]; + + document.getElementById('cfg_info_name').innerHTML = item[0]; + document.getElementById('cfg_info_comp').innerHTML = item[1]; + document.getElementById('cfg_info_year').innerHTML = item[2]; + styleDisplayTable('cfg_info', 1); + styleDisplayInline('dskchg_grp', item[4] ? 1 : 0); + + if (item[6].length) { + document.getElementById('cfg_info_load').innerHTML = item[6]; + styleDisplayTableRow('cfg_info_load_grp', 1); + } else + styleDisplayTableRow('cfg_info_load_grp', 0); + + if (grp == 2) { + styleDisplayTableRow('cfg_info_ctrl_grp', 0); + } else { + var keys = []; + if (item[5][0]) { + var input = [ + ['Movement','Arrows'], + ['Fire 1',fireButtonName(item[5][1])], + ['Fire 2',fireButtonName(item[5][2])] + ]; + keys = keys.concat(input); + } + if (item[7].length) + keys = keys.concat(item[7]); + + var ctrl = ''; + for (var i = 0; i < keys.length; i++) + ctrl += keys[i][0]+': '+keys[i][1]+'
'; + + var ctrl = ''; + for (var i = 0; i < keys.length; i++) + ctrl += ''; + ctrl += '
'+keys[i][0]+':'+keys[i][1]+'
'; + + document.getElementById('cfg_info_ctrl').innerHTML = ctrl; + styleDisplayTableRow('cfg_info_ctrl_grp', 1); + } +} + +/*-----------------------------------------------------------------------*/ +/* advanced config */ + +function romAROS() { + //document.getElementById('cfg_rom_aros').innerHTML = 'Loading...'; + disabled('cfg_rom_aros', 1); + + config.rom.name = aros_rom_file; + config.rom.size = SAEV_Config_ROM_Size_512K; + if ((config.rom.data = cache.loadRom(0)) === null) + return false; + + config.ext.name = aros_ext_file; + config.ext.size = SAEV_Config_EXT_Size_512K; + config.ext.addr = SAEV_Config_EXT_Addr_E0; + if ((config.ext.data = cache.loadRom(1)) === null) + return false; + + /*document.getElementById('cfg_rom_aros').style.visibility = 'hidden';*/ + + setRomName(config.rom.name); + setExtName(config.ext.name); + document.getElementById('cfg_ext_addr')[0].selected = true; + styleDisplayInline('cfg_ext_remove', 1); + styleDisplayTableRow('cfg_ext_addr_grp', 1); + disabled('cfg_rom_aros', 0); +} + +function romSelect() { + var e = document.getElementById('cfg_rom_file').files[0]; + if (!e) return; + if (!(e.size == 0x40000 || e.size == 0x80000)) { + alert('Invalid rom-size, 256 or 512kb.'); + return; + } + loadLocal(e, function (event) { + /* + document.getElementById('cfg_rom_aros').style.visibility = 'visible'; + document.getElementById('cfg_rom_aros').innerHTML = 'Set AROS'; + disabled('cfg_rom_aros', 0);*/ + config.rom.name = e.name; + config.rom.size = e.size == 0x40000 ? SAEV_Config_ROM_Size_256K : SAEV_Config_ROM_Size_512K; + config.rom.data = event.target.result; + setRomName(config.rom.name); + }); +} + +function extSelect() { + var e = document.getElementById('cfg_ext_file').files[0]; + if (!e) return; + if (!(e.size == 0x40000 || e.size == 0x80000)) { + alert('Invalid extended rom-size, 256 or 512kb.'); + return; + } + loadLocal(e, function (event) { + /*document.getElementById('cfg_rom_aros').style.visibility = 'visible'; + document.getElementById('cfg_rom_aros').innerHTML = 'Set AROS'; + disabled('cfg_rom_aros', 0);*/ + config.ext.name = e.name; + config.ext.size = e.size == 0x40000 ? SAEV_Config_EXT_Size_256K : SAEV_Config_EXT_Size_512K; + config.ext.data = event.target.result; + setExtName(config.ext.name); + styleDisplayInline('cfg_ext_remove', 1); + styleDisplayTableRow('cfg_ext_addr_grp', 1); + }); +} + +function extRemove() { + /*document.getElementById('cfg_rom_aros').style.visibility = 'visible'; + document.getElementById('cfg_rom_aros').innerHTML = 'Set AROS'; + disabled('cfg_rom_aros', 0);*/ + config.ext.name = null; + config.ext.size = SAEV_Config_EXT_Size_None; + config.ext.data = null; + setExtName(config.ext.name); + styleDisplayInline('cfg_ext_remove', 0); + styleDisplayTableRow('cfg_ext_addr_grp', 0); +} + +function floppyUpdate(n) { + if (document.getElementById('cfg_df'+n+'_enabled').checked) { + styleDisplayInline('cfg_df'+n+'_grp', 1); + + var e = document.getElementById('cfg_df'+n+'_type'); + config.floppy.drive[n].type = parseInt(getSelectValue(e)); + } else { + styleDisplayInline('cfg_df'+n+'_grp', 0); + floppyEject(n); + config.floppy.drive[n].type = SAEV_Config_Floppy_Type_None; + } +} + +function floppyInsert(n) { + var e = document.getElementById('cfg_df'+n+'_file').files[0]; + var ok = false; + + if (!e) return; + /*if (e.size == 0xDC000) { + if (AMIGA.config.floppy.drive[n].type == SAEV_Config_Floppy_Type_35_DD) + ok = true; + else + alert('DF'+n+' is configured as HD-drive (1760kb), but you selected a DD-diskimage (880kb).'); + } + else if (e.size == 0x1B8000) { + if (AMIGA.config.floppy.drive[n].type == SAEV_Config_Floppy_Type_35_HD) + ok = true; + else + alert('DF'+n+' is configured as DD-drive (880kb), but you selected a HD-diskimage (1760kb).'); + } else + alert('Invalid diskimage-size, 880 or 1460 kb.'); + */ + ok = true; + if (ok) + loadLocal(e, function (event) { + config.floppy.drive[n].name = e.name; + config.floppy.drive[n].data = event.target.result; + setFloppyName(n, config.floppy.drive[n].name); + styleDisplayInline('cfg_df'+n+'_eject', 1); + }); +} + +function floppyEject(n) { + config.floppy.drive[n].name = null; + config.floppy.drive[n].data = null; + setFloppyName(n, null); + styleDisplayInline('cfg_df'+n+'_eject', 0); +} + +function audioUpdate() { + styleDisplayTable('cfg_audio_grp', document.getElementById('cfg_audio_enabled').checked); +} + +function videoUpdate() { + styleDisplayBlock('cfg_video_grp', document.getElementById('cfg_video_enabled').checked); +} + +function spritesUpdate() { + styleDisplayInline('cfg_chipset_cl_grp', document.getElementById('cfg_chipset_cl_enabled').checked); +} + +function keyboradUpdate() { + styleDisplayBlock('cfg_keyborad_grp', document.getElementById('cfg_keyborad_enabled').checked); +} + +function portUpdate(n) { + var v = document.getElementById('cfg_ports_'+n+'_enabled').checked; + styleDisplayInline('cfg_ports_'+n+'_grp', v); + if (n == 0) { + if (v) + portUpdate2(); + else + styleDisplayInline('cfg_ports_0_grp2', 0); + } +} +function portUpdate2() { + var e = document.getElementById('cfg_ports_0'); + var v = parseInt(getSelectValue(e)); + styleDisplayInline('cfg_ports_0_grp2', v == 2); +} + +/*-----------------------------------------------------------------------*/ +/* status hooks */ + +function hooks_error(err, msg) { + stop(); + alert(msg); +} + +function hooks_power_led(on) { + var e = document.getElementById('led_pwr'); + if (e) e.style.color = on ? '#8c8' : '#888'; +} +function hooks_floppy_motor(unit, on) { + var e; + switch (unit) { + case 0: e = document.getElementById('led_df0'); break; + case 1: e = document.getElementById('led_df1'); break; + case 2: e = document.getElementById('led_df2'); break; + case 3: e = document.getElementById('led_df3'); break; + } + if (e) e.style.color = on ? '#8c8' : '#888'; +} +function hooks_floppy_step(unit, cyl) { + var e; + switch (unit) { + case 0: e = document.getElementById('led_df0'); break; + case 1: e = document.getElementById('led_df1'); break; + case 2: e = document.getElementById('led_df2'); break; + case 3: e = document.getElementById('led_df3'); break; + } + if (e) e.innerHTML = cyl; +} +function hooks_fps(fps) { + var e = document.getElementById('led_fps'); + if (e) e.innerHTML = fps;//+'/'+(config.video.ntsc?'60.0':'50.0'); +} +function hooks_cpu(usage) { + var e = document.getElementById('led_cpu'); + if (e) { + e.style.color = usage <= 100 ? '#8c8' : (usage > 100 && usage < 120 ? '#cc8' : '#d88'); + e.innerHTML = usage+'%'; + } +} + +/*-----------------------------------------------------------------------*/ +/* disk change */ + +function dskchgOpen() { + if (!dskchg) { + if (mode == 0) { + var s = document.getElementById('cfg_dskchg_select'); + for (var i = 0; i < dskchgList.length; i++) { + var filename = dskchgList[i]; + var e = document.createElement('option'); + e.value = filename; + e.text = filename; + s.add(e, null); + } + styleDisplayBlock('dskchg_simple', 1); + } else + styleDisplayBlock('dskchg', 1); + + dskchg = true; + } else + dskchgClose(); +} + +function dskchgClose() { + if (dskchg) { + if (mode == 0) { + styleDisplayBlock('dskchg_simple', 0); + var s = document.getElementById('cfg_dskchg_select'); + for (var i = s.length - 1; i > 0; i--) + s.remove(i); + } else + styleDisplayBlock('dskchg', 0); + + dskchg = false; + } +} + +function dskchgEject() { + if (dskchg) { + var n = getSelectValue(document.getElementById('cfg_dskchg_unit')); + dskchgClose(); + + SAE({cmd:'eject',unit:n}); + floppyEject(n); + } +} + +function dskchgInsert() { + if (!dskchg) return; + var n = getSelectValue(document.getElementById('cfg_dskchg_unit')); + var e = document.getElementById('cfg_dskchg_file').files[0]; + var ok = false; + + if (!e) return; + /*if (e.size == 0xDC000) { + if (AMIGA.config.floppy.drive[n].type == SAEV_Config_Floppy_Type_35_DD) + ok = true; + else + alert('DF'+n+' is configured as HD-drive (1760kb), but you selected a DD-diskimage (880kb).'); + } else if (e.size == 0x1B8000) { + if (AMIGA.config.floppy.drive[n].type == SAEV_Config_Floppy_Type_35_HD) + ok = true; + else + alert('DF'+n+' is configured as DD-drive (880kb), but you selected a HD-diskimage (1760kb).'); + } else + alert('Invalid diskimage-size, 880 or 1460 kb.'); + */ + ok = true; + if (ok) { + loadLocal(e, function (event) { + dskchgClose(); + + setFloppyName(n, e.name); + styleDisplayInline('cfg_df'+n+'_eject', 1); + + config.floppy.drive[n].type = SAEV_Config_Floppy_Type_35_DD; + config.floppy.drive[n].name = e.name; + config.floppy.drive[n].data = event.target.result; + + /*SAE({ + cmd:'insert', + unit:n, + name:e.name, + data:event.target.result + });*/ + SAE({ + cmd:'insert', + unit:n + }); + }); + } +} + +function dskchgSelect() { + if (!dskchg) return; + var filename = getSelectValue(document.getElementById('cfg_dskchg_select')); + var url = 'http://'+window.location.hostname+'/db/' + (dbGrp == 1 ? 'games/' : 'demos/') + filename + '.adf'; + var n = 0; + + if ((config.floppy.drive[n].data = cache.loadDisk(url)) !== null) { + dskchgClose(); + config.floppy.drive[n].type = SAEV_Config_Floppy_Type_35_DD; + config.floppy.drive[n].name = filename; + + SAE({ + cmd:'insert', + unit:n + }); + } +} diff --git a/readme.htm b/readme.htm new file mode 100644 index 0000000..79286dd --- /dev/null +++ b/readme.htm @@ -0,0 +1,388 @@ + + + + SAE - Scripted Amiga Emulator + + + + + + +
+
+ Scripted Amiga Emulator + +
+
+
+
+ + + + + +
+
    +
  • Requirements +
      +
    • A very fast computer
    • +
    • A browser capable of HTML5 with: +
        +
      • WegGL
      • +
      • WebAudio
      • +
      • Typed arrays
      • +
      • FileReader (GUI)
      • +
      +
    • +
    • A ROM: +
        +
      • Amiga Kickstart 1.x-2.04 (not included for copyright reasons)
      • +
      • AROS Kickstart (included)
      • +
      +
    • +

    • +
    • And a bunch of ADF-files to test (optional)
    • +
    +
  • +
+
+ + + + + + + +
Supported browsers
ChromeFastest
FirefoxMedium speed
OperaSlow speed, no audio
IENo support
OtherUntested
+
+
+
    +
  • Goals +
      +
    • Make Amiga Classic Emulation available for browsers.
    • +
    • Easy integration into websites.
    • +
    • API for control from background, useful e.g. for a demo-player.
    • +
    • Browsers-plugins should not be required, just HTML5 and javascript.
    • +
    +
  • +
+
    +
  • Possible usage +
      +
    • Demo/Intro/Mod-player
    • +
    • Playing games
    • +
    • Browser benchmark
    • +
    +
  • +
+
    +
  • Usage/Hints +
      +
    • Press F11 for fullscreen. Most browsers support that.
    • +
    • If something does crash, try another memory setting or a different ROM. The original kickstart (1.3) work's best.
    • +
    +
  • +
+
    +
  • Known bugs/problems +
      +
    • Fast blinking sprites are not displayed if 'Frameskip' is active.
    • +
    • Copper initiated color-changes within a scanline are not recognized.
    • +
    • Left/right shift/alt/ctrl are the same. That's a browser 'feature' and can't be fixed for now.
    • +
    +
  • +
+
    +
  • Missing features +
      +
    • User-config
    • +
    • Maybe ECS support
    • +
    +
  • +
+ + +
    +
  • Thanks +
      +
    • Bernd Schmidt
    • +
    • WinUAE and AROS team.
    • +
    +
  • +
+
    +
  • License +
      +
    • This is a free project.
    • +
    • You may use the emulator in your own website.
    • +
    • If you use the emulator in your own website, you must provide a link to this homepage.
    • +
    • If you use the emulator in your own website, you must not change the copyright header in the source-files.
    • +
    • Commercial use in any form is prohibited. This includes donations.
    • +
    • No warranty expressed or implied. Use at your own risk.
    • +
    +
  • +
+
+ + + + + + + + +
Fully working...
+
    +
  • Games +
      +
    • Alien Breed
    • +
    • Alien Breed Special Edition 92 (0.7)
    • +
    • Alien Breed Tower Assault (0.6)
    • +
    • Alien Breed II - The horror continues (0.7)
    • +
    • Boby Blows Galactic (0.7)
    • +
    • Agony (0.6.1)
    • +
    • Another World
    • +
    • Barbarian (0.7)
    • +
    • Battle Chess
    • +
    • Blood Money
    • +
    • Crystal Hammer
    • +
    • Dynablaster (0.7)
    • +
    • Firepower
    • +
    • Lotus Turbo Challenge 1/2 (0.6)
    • +
    • NorthPrince of Persia (0.7)
    • +
    • Pinball Dreams
    • +
    • Pinball Fantasies (the demo with 1 desk)
    • +
    • Prince of Persia (0.7)
    • +
    • Project-X (0.7)
    • +
    • Project-X Special Edition '92 (0.6)
    • +
    • P.O.W.
    • +
    • Rubicon (0.6)
    • +
    • R-Type 2
    • +
    • Shadow of the Beast 2 (0.6)
    • +
    • Terminator 2 (0.7)
    • +
    • Turrican 1/2
    • +
    • Turrican 3 (0.6)
    • +
    • Wings (0.6)
    • +
    • Wings of Death (0.6.1)
    • +
    +
  • +
+
+
    +
  • Demos +
      +
    • Deja Vu
    • +
    • How to skin a cat
    • +
    • Project-X (demo-rolling) (0.6)
    • +
    • State of the Art
    • +
    +
  • +
+
+
    +
  • Apps +
      +
    • Devpak 2.14
    • +
    • DPaint 4.1
    • +
    • Protracker 3.61
    • +
    • SysInfo 3.24
    • +
    • Workbench 1.x
    • +
    • Workbench 2.04 (0.6)
    • +
    +
  • +
+
Note: The lists corresponds to Kickstart 1.3
+
+
    +
  • History +
      +
    • 0.7.0 (24.12.2012): +
        +
      • Common +
          +
        • Chaned the project-name from 'Janus' to 'SAE' (Scripted Amiga Emulator).
          + This step is necessary because i did'nt enough research before the initial release.
          + There is already a project with that name in it. (janus-uae) + I'm sorry for that, that was kinda stupid :)
        • +
        • Separated the GUI form the core and rewrote most of it.
        • +
        • Simplified the whole code. This means, that functions are called faster.
        • +
        • Ported new Copper-, Blitter-, Audio-, Disk- and event-handling code from WinUAE 2.5.0
        • +
        • Moved all "beam-functions" to events.js, for faster access.
        • +
        • Added "CPU-usage" to the GUI.
        • +
        • Added "Hall of Fame".
        • +
        +
      • +
      • CPU +
          +
        • Added support for exact cycling. The CPU have now 7.09 MHz on PAL and 7.16 MHz on NTSC resp.
        • +
        • Added option for "Original"- and "Maximum"-speed.
        • +
        +
      • +
      • Audio +
          +
        • Added a ring-buffer before output. No more "bagpipe"-like audio.
        • +
        • Added a option to just emulate the hardware, but not to play any sound, e.g. "Agony" was not starting, if audio was disabled.
        • +
        +
      • +
      • Display +
          +
        • Switched from the RGB to the 565 texture-format-encoding in WebGL, which gives a little more speed.
        • +
        • Added support and a option for 4x scaling.
        • +
        • Fixed screen-draging in lo-/hires-modes.
        • +
        • Added a option to disable the output. Can be used to better play music-demos.
        • +
        +
      • +
      • Disk +
          +
        • Added support for different speed-modes.
        • +
        • Added support for extended ADF-files.
        • +
        • Enabled write support. (to memory)
        • +
        +
      • +
      • Input +
          +
        • The F-keys does work normaly now, i.e F5 does'nt reload the emulator, backspace does'nt go to the last page, etc...
        • +
        • Fixed bug in the joystick-emulation.
        • +
        +
      • +
      +
    • +

    • +
    • 0.6.1 (05.11.2012): +
        +
      • CPU +
          +
        • Added missing TAS command. ('Wings of Death' game does use it)
        • +
        +
      • +
      • ROM +
          +
        • Added a crc32-checksum test after download. (AROS)
        • +
        • Changed transfer-mode from syncronous to asyncronous. (AROS)
        • +
        +
      • +
      • Display +
          +
        • Optimized the line-drawing code of the blitter.
        • +
        • Fixed decision whether the resulting image is scaled or not. ('Agony' game)
        • +
        • Disabled 'Framedrop' in interlace-mode.
        • +
        +
      • +
      • Misc +
          +
        • Fixed HTML5 doctype.
        • +
        • Added ver/rev to the title.
        • +
        • Changed revision to 3 digits.
        • +
        +
      • +
      +
    • +

    • +
    • 0.6 (25.10.2012): +
        +
      • ROM +
          +
        • Added support for extended-roms.
        • +
        • AROS kickstart replacement is now the default-rom, and will be downloaded and used on the fly.
        • +
        +
      • +
      • RAM +
          +
        • Changed internal memory alignment from 8 to 16 bit.
        • +
        • Added fast-ram support, through a emulated Commodore A2058 in Zorro2-mode.
        • +
        • Removed the D8DC-memory-option from the GUI. This memory space is now always allocated.
        • +
        • Sorted access of the memory-routines by priority.
        • +
        +
      • +
      • CPU +
          +
        • Fixed sign-extension for memory to address-register transfers in MOVEM.
        • +
        • Fixed sign-extension for absolute-word addresses in exEA.
        • +
        • Fixed wrong address when writing a byte to the stack in ldEA.
        • +
        • Fixed 32bit calculations for address-regsiters in ADDQ/SUBQ.
        • +
        • Fixed remainder calculation in DIVU.
        • +
        • Optimized integer-arithmetic functions. Size depending calcalations are now used.
        • +
        • Improved exception 2/3 handling.
        • +
        • More small cleanups not listed here.
        • +
        +
      • +
      • Disk +
          +
        • Ported latest disk-code from WinUAE. Disk-based problems should be gone now, e.g. floopy shown as BAD or read errors.
        • +
        • Diskchange does now work.
        • +
        • Drives can be disabled now.
        • +
        • DD/HD types are now supported.
        • +
        +
      • +
      • Display +
          +
        • Added missing support for HAM6.
        • +
        • Fixed horizontal and vertical screen-centering.
        • +
        • Fixed screen-draging, almost, read the "Known bugs" section.
        • +
        • Added better screen-scrolling.
        • +
        +
      • +
      • Input +
          +
        • Fixed key-mapping to the amiga key-layout, at least in Chrome. Firefox does not like "öäü", it seems.
        • +
        • Added support for individual configurations of joystick-movement and fire-buttons.
        • +
        • Fixed bug, that made the fire 1 button on joystick 0 not working.
        • +
        • Fixed bug in the mouse-movement function, that calculates the wrong coordinates in some cases.
        • +
        +
      • +
      • GUI +
          +
        • Added eject-button in the disk-change requester.
        • +
        • Removed the CPU and OCS/ECS options. They are useless so far.
        • +
        • The "Pause" and "Resume" buttons are once now.
        • +
        • Made some options to only apear when selected.
        • +
        • Did some cosmetics.
        • +
        +
      • +
      • Misc +
          +
        • Added support for Zorro2-expansion cards.
        • +
        • Added a more precise event-handling. This makes the audio-output a little smoother.
        • +
        • Changed the ratio of CPU-/Chipset-time from 1:8 to 1:4
        • +
        • Fixed a bug in the RTC. "Year" is now calculated correctly.
        • +
        • Fixed incorrect response of serial.device. Now crashed programms does reset (and show the guru), instead of idle.
        • +
        • Any output on the serial.device is now redirected to the debug-log. (dev only)
        • +
        • Kickstart/Workbench 2.04 and AROS does now work.
        • +
        • Overall about 10-30% more speed, as long as the blitter is not used too much.
        • +
        +
      • +
      +
    • +

    • +
    • 0.5 (01.09.2012): +
        +
      • Initial.
      • +
      +
    • +
    +
  • +
+
+
+ + + diff --git a/sae/amiga.js b/sae/amiga.js new file mode 100644 index 0000000..84b8fc7 --- /dev/null +++ b/sae/amiga.js @@ -0,0 +1,459 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function set_special(x) { AMIGA.spcflags |= x; } +function clr_special(x) { AMIGA.spcflags &= ~x; } + +function Amiga() { + this.config = new Config(); + this.mem = new Memory(); + this.expansion = new Expansion(); + this.input = new Input(); + this.serial = new Serial(); + this.events = new Events(); + this.disk = new Disk(); + this.cia = new CIA(); + this.rtc = new RTC(); + this.custom = new Custom(); + this.blitter = new Blitter(); + this.copper = new Copper(); + this.playfield = new Playfield(); + this.video = new Vide0(); + this.audio = new Audi0(); + this.cpu = new CPU(); + + this.state = CMD_STOP; + this.delay = 0; + this.spcflags = 0; + //this.loading = 0; + + this.intena = 0; + this.intena_internal = 0; + this.intreq = 0; + this.intreq_internal = 0; + this.dmacon = 0; + this.adkcon = 0; + + configSetDefaults(this.config); + + /*---------------------------------*/ + + this.setup = function() { + if (this.config.chipset.type == SAEV_Config_Chipset_Type_OCS) + this.config.chipset.agnus = this.config.video.ntsc ? AGNUS_8370 : AGNUS_8371; + + this.mem.setup(); + this.expansion.setup(); + this.events.setup(); + this.cia.setup(); + this.rtc.setup(); + this.input.setup(); + this.disk.setup(); + this.playfield.setup(); + this.video.setup(); + this.audio.setup(); + this.custom.setup(); + this.cpu.setup(); + } + + this.cleanup = function() { + this.audio.cleanup(); + this.video.cleanup(); + this.playfield.cleanup(); + this.input.cleanup(); + } + + this.reset = function() { + BUG.info('Amiga.reset()'); + + this.delay = 0; + //this.loading = 0; + + this.intena = 0; + this.intena_internal = 0; + this.intreq = 0; + this.intreq_internal = 0; + this.dmacon = 0; + this.adkcon = 0; + + this.expansion.reset(); + this.events.reset(); + this.cia.reset(); + this.disk.reset(); + this.input.reset(); + this.serial.reset(); + this.blitter.reset(); + this.copper.reset(); + this.playfield.reset(); + this.audio.reset(); + this.custom.reset(); + this.cpu.reset(this.mem.rom.lower); + } + + this.dump = function() { + this.cpu.dump(); + //this.cia.dump(); + } + + /*---------------------------------*/ + + /*this.waitForStart = function() { + if (this.loading) + setTimeout('AMIGA.waitForStart()', 10); + else { + this.reset(); + this.state = CMD_CYCLE; + setTimeout('AMIGA.cycle()', 0); + } + } + this.start = function() { + this.setup(); + this.waitForStart(); + }*/ + + + this.start = function() { + if (this.state == CMD_STOP) { + this.setup(); + this.reset(); + this.state = CMD_CYCLE; + setTimeout('AMIGA.cycle()', 0); + } + } + + this.stop = function() { + if (this.state != CMD_STOP) { + this.state = CMD_STOP; + this.cleanup(); + } + } + + this.pause = function(state) { + if (this.state != CMD_STOP) { + this.state = state ? CMD_PAUSE : CMD_CYCLE; + this.audio.pauseResume(state); + } + } + + /*this.insert = function(unit, name, data) { + //this.disk.insert_data(unit, data); + this.disk.insert(unit, name, data); + this.config.floppy.drive[unit].name = name; + } + this.eject = function(unit) { + if (this.config.floppy.drive[unit].name) { + this.disk.eject(unit); + //this.disk.eject_data(unit); + this.config.floppy.drive[unit].name = null; + BUG.info('amiga.eject() DF%d ejected', unit); + } else + BUG.info('amiga.eject() DF%d in empty', unit); + } + */ + + this.insert = function(unit) { + if (this.state != CMD_STOP) + this.disk.insert(unit); + } + + this.eject = function(unit) { + if (this.state != CMD_STOP) + this.disk.eject(unit); + } + + /*---------------------------------*/ + /* mainloop */ + + this.cycle = function() { + try { + while (this.state == CMD_CYCLE) + this.cpu.cycle(); + } catch (e) { + if (e instanceof FatalError) { + this.stop(); + this.config.hooks.error(e.error, e.message); + } else + console.log(e); + } + + if (this.state == CMD_IDLE) { + this.state = CMD_CYCLE; + setTimeout('AMIGA.cycle()', this.delay); + } + else if (this.state == CMD_PAUSE) + setTimeout('AMIGA.cyclePause()', 0); + else + setTimeout('AMIGA.cycleExit()', 0); + } + + this.cyclePause = function() { + if (this.state == CMD_CYCLE) + setTimeout('AMIGA.cycle()', 0); + else if (this.state == CMD_PAUSE) + setTimeout('AMIGA.cyclePause()', 500); + else + setTimeout('AMIGA.cycleExit()', 0); + } + + this.cycleExit = function() { + this.dump(); + } + + /*---------------------------------*/ + + this.dmaen = function (dmamask) { + return ((this.dmacon & DMAF_DMAEN) && (this.dmacon & dmamask)); + } + + this.DMACONR = function(hpos) { + this.dmacon &= ~(0x4000 | 0x2000); + //this.dmacon |= (this.blitter.state == BLT_STOP ? 0 : 0x4000) | (this.blitter.zero ? 0x2000 : 0); //old + //this.dmacon |= ((blit_interrupt || (!blit_interrupt && currprefs.cs_agnusbltbusybug && !blt_info.got_cycle)) ? 0 : 0x4000) | (blt_info.blitzero ? 0x2000 : 0); //new org + var iz = this.blitter.getIntZero(); + this.dmacon |= (iz[0] ? 0 : 0x4000) | (iz[1] ? 0x2000 : 0); + return this.dmacon; + } + + this.DMACON = function (v, hpos) { + var oldcon = this.dmacon; + + if (v & INTF_SETCLR) + this.dmacon |= v & ~INTF_SETCLR; + else + this.dmacon &= ~v; + + this.dmacon &= 0x1fff; + + var changed = this.dmacon ^ oldcon; + + var oldcop = (oldcon & DMAF_COPEN) && (oldcon & DMAF_DMAEN); + var newcop = (this.dmacon & DMAF_COPEN) && (this.dmacon & DMAF_DMAEN); + if (oldcop != newcop) { + if (newcop && !oldcop) { + this.copper.compute_spcflag_copper(this.events.hpos()); + } else if (!newcop) { + this.copper.enabled_thisline = false; + clr_special (SPCFLAG_COPPER); + } + } + //if ((this.dmacon & DMAF_COPEN) > (oldcon & DMAF_COPEN)) + //this.copper.COPJMP(1, 0); + + if ((this.dmacon & DMAF_SPREN) > (oldcon & DMAF_SPREN)) { + for (var i = 0; i < 8; i++) + this.playfield.sprites.sprite[i].dmastate = 1; + } + if ((this.dmacon & DMAF_BLTPRI) > (oldcon & DMAF_BLTPRI) && this.blitter.getState() != BLT_done) + set_special(SPCFLAG_BLTNASTY); + if (this.dmaen(DMAF_BLTEN) && this.blitter.getState() == BLT_init) + this.blitter.setState(BLT_work); + if ((this.dmacon & (DMAF_BLTPRI | DMAF_BLTEN | DMAF_DMAEN)) != (DMAF_BLTPRI | DMAF_BLTEN | DMAF_DMAEN)) + clr_special(SPCFLAG_BLTNASTY); + + if (changed & (DMAF_DMAEN | 0x0f)) + this.audio.state_machine(); + + /*if (this.copper.active && !this.events.eventtab[EV_COPPER].active) { + this.events.eventtab[EV_COPPER].active = true; + this.events.eventtab[EV_COPPER].oldcycles = this.events.currcycle; + this.events.eventtab[EV_COPPER].evtime = 1 * CYCLE_UNIT + this.events.currcycle; + this.events.schedule(); + }*/ + } + + /*---------------------------------*/ + + this.ADKCONR = function () { + return this.adkcon; + } + + this.ADKCON = function (v, hpos) { + if (this.config.audio.enabled) + this.audio.update(); + + this.disk.update(hpos); + this.disk.update_adkcon(v); + + if (v & INTF_SETCLR) + this.adkcon |= v & ~INTF_SETCLR; + else + this.adkcon &= ~v; + + this.audio.update_adkmasks(); + } + + /*---------------------------------*/ + + this.INTENAR = function () { + return this.intena; + } + + this.INTENA = function(v) { + var old = this.intena; + + if (v & INTF_SETCLR) + this.intena |= v & ~INTF_SETCLR; + else + this.intena &= ~v; + + if (!(v & INTF_SETCLR) && old == this.intena) + return; + + if (AMIGA.config.cpu.exact) { + this.events.event2_newevent_xx(-1, INT_PROCESSING_DELAY, this.intena, function(v) { + //console.log('INTENA()', v); + AMIGA.intena_internal = v; + AMIGA.doint(); + }); + } else { + this.intena_internal = this.intena; + if (v & INTF_SETCLR) + this.doint(); + } + } + + /*---------------------------------*/ + + this.INTREQR = function () { + return this.intreq; + } + + this.INTREQ_0 = function(v) { + var old = this.intreq; + + if (v & INTF_SETCLR) + this.intreq |= v & ~INTF_SETCLR; + else + this.intreq &= ~v; + + if (AMIGA.config.cpu.exact) { + if (old == this.intreq && this.intreq_internal == this.intreq) + return; + + this.events.event2_newevent_xx(-1, INT_PROCESSING_DELAY, this.intreq, function(v) { + //console.log('INTREQ_0()', v); + AMIGA.intreq_internal = v; + AMIGA.doint(); + }); + } else { + this.intreq_internal = this.intreq; + if (this.intreq == old) + return; + if (v & INTF_SETCLR) + this.doint(); + } + } + + this.INTREQ = function(v) { + this.INTREQ_0(v); + this.cia.reThink(); + } + + this.intlev = function() { + var imask = this.intreq_internal & this.intena_internal; + + if (imask && (this.intena_internal & INTF_INTEN)) { + if (imask & 0x2000) return 6; + if (imask & (0x1000 | 0x0800)) return 5; + if (imask & (0x0400 | 0x0200 | 0x0100 | 0x0080)) return 4; + if (imask & (0x0040 | 0x0020 | 0x0010)) return 3; + if (imask & 0x0008) return 2; + if (imask & (0x0001 | 0x0002 | 0x0004)) return 1; + } + return -1; + } + + this.doint = function() { + if (AMIGA.config.cpu.exact) { + this.cpu.setIPL(this.intlev()); + clr_special(SPCFLAG_INT); + return; + } + if (AMIGA.config.cpu.compatible) + set_special(SPCFLAG_INT); + else + set_special(SPCFLAG_DOINT); + } + + this.send_interrupt = function(num, delay) { + if (AMIGA.config.cpu.exact && delay > 0) { + if (!(this.intreq & (1 << num))) { + this.events.event2_newevent_xx(-1, delay, num, function(v) { + AMIGA.INTREQ_0(INTF_SETCLR | (1 << v)); + }); + } + } else + this.INTREQ_0(INTF_SETCLR | (1 << num)); + } +} + +/*-----------------------------------------------------------------------*/ +/* This API will change in the future. */ + +function SAE(x) { + try { + switch (x.cmd) { + case 'init': + BUG = new Debug(); + BUG.info('API.init() SEA %d.%d.%d', SAEV_Version, SAEV_Revision, SAEV_Revision_Sub); + + AMIGA = new Amiga(); + return AMIGA.config; + case 'reset': + BUG.info('API.reset()'); + AMIGA.reset(); + break; + case 'start': + BUG.info('API.start()'); + AMIGA.start(); + break; + case 'stop': + BUG.info('API.stop()'); + AMIGA.stop(); + break; + case 'reset': + BUG.info('API.reset()'); + AMIGA.reset(); + break; + case 'pause': + BUG.info('API.pause() %d', x.state); + AMIGA.pause(x.state); + break; + /*case 'insert': + BUG.info('API.insert() DF%d, name "%s", length %d', x.unit, x.name, x.data.length); + AMIGA.insert(x.unit, x.name, x.data); + break;*/ + case 'insert': + BUG.info('API.insert() DF%d', x.unit); + AMIGA.insert(x.unit); + break; + case 'eject': + BUG.info('API.eject() DF%d', x.unit); + AMIGA.eject(x.unit); + break; + /*case 'getConfig': + BUG.info('API.getConfig()'); + return AMIGA.config; + case 'setConfig': + BUG.info('API.setConfig() size '+x.data.ext.size); + AMIGA.config = x.data; + break;*/ + } + } catch (e) { + if (e instanceof FatalError) { + AMIGA.stop(); + //return { error:e.error, message:e.message }; + AMIGA.config.hooks.error(e.error, e.message); + } else + console.log(e); + } + //return SAEE_None; + //return { error:SAEE_None, message:'' }; +} diff --git a/sae/audio.js b/sae/audio.js new file mode 100644 index 0000000..b5945ef --- /dev/null +++ b/sae/audio.js @@ -0,0 +1,1126 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +/*const DEBUG_CHANNEL_MASK = 15 +function debugchannel(ch) { + return ((1 << ch) & DEBUG_CHANNEL_MASK) != 0; +}*/ + +function Filter() { + const DENORMAL_OFFSET = 1E-10; + + this.on = false; + this.led_filter_on = false; + + var filter_state = [ + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 }, + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 }, + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 }, + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 } + ]; + var filter1_a0 = 0; + var filter2_a0 = 0; + var filter_a0 = 0; + + function getRate(v) { + switch (v) { + case SAEV_Config_Audio_Rate_11025: return 11025; + case SAEV_Config_Audio_Rate_22050: return 22050; + case SAEV_Config_Audio_Rate_44100: return 44100; + case SAEV_Config_Audio_Rate_48000: return 48000; + default: return 44100; + } + } + + function calc(sample_rate, cutoff_freq) { + if (cutoff_freq >= sample_rate / 2) + return 1.0; + + var omega = 2 * Math.PI * cutoff_freq / sample_rate; + omega = Math.tan(omega / 2) * 2; + return 1 / (1 + 1 / omega); + } + + this.setup = function(on) { + this.on = on; + var rate = getRate(AMIGA.config.audio.rate); + filter1_a0 = calc(rate, 6200); + filter2_a0 = calc(rate, 20000); + filter_a0 = calc(rate, 7000); + /*console.log(rate); + console.log(filter1_a0); + console.log(filter2_a0); + console.log(filter_a0);*/ + } + + this.reset = function() { + filter_state = [ + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 }, + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 }, + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 }, + { rc1:0,rc2:0,rc3:0,rc4:0,rc5:0 } + ]; + } + + this.filter = function(input, state) { + if (!this.on) + return input; + + var o, fs = filter_state[state]; + + fs.rc1 = filter1_a0 * input + (1 - filter1_a0) * fs.rc1 + DENORMAL_OFFSET; + fs.rc2 = filter2_a0 * fs.rc1 + (1 - filter2_a0) * fs.rc2; + var no = fs.rc2; + + if (this.led_filter_on) { + fs.rc3 = filter_a0 * no + (1 - filter_a0) * fs.rc3; + fs.rc4 = filter_a0 * fs.rc3 + (1 - filter_a0) * fs.rc4; + fs.rc5 = filter_a0 * fs.rc4 + (1 - filter_a0) * fs.rc5; + o = fs.rc5; + } else + o = no; + + if (o > 32767) o = 32767; else if (o < -32768) o = -32768; + return o; + } +} + +function Channel(num) { + this.num = num; + this.adk_mask = 0; + this.enabled = false; + this.evtime = 0; + this.dmaenstore = false; + this.intreq = false; + this.dr = false; + this.dsr = false; + this.pbufldl = false; + this.dat_written = false; + this.state = 0; + this.lc = 0; + this.pt = 0; + this.per = 0; + this.vol = 0; + this.len = 0; + this.wlen = 0; + this.dat = 0; + this.dat2 = 0; + this.current_sample = 0; + this.last_sample = 0; + this.ptx = 0; + this.ptx_written = false; + this.ptx_tofetch = false; + + this.reset = function() { + this.adk_mask = 0; + this.enabled = false; + this.evtime = CYCLE_MAX; + this.dmaenstore = false; + this.intreq = false; + this.dr = false; + this.dsr = false; + this.pbufldl = false; + this.dat_written = false; + this.state = 0; + this.lc = 0; + this.pt = 0; + this.per = PERIOD_MAX - 1; + this.vol = 0; + this.len = 0; + this.wlen = 0; + this.dat = 0; + this.dat2 = 0; + this.current_sample = 0; + this.last_sample = 0; + this.ptx = 0; + this.ptx_written = false; + this.ptx_tofetch = false; + } + + //const audio_channel_mask = 15; + this.newsample = function(sample) { + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].newsample() %02x', nr, sample); + //if (!(audio_channel_mask & (1 << this.num))) sample = 0; + if (sample & 0x80) sample -= 0x100; + this.last_sample = this.current_sample; + this.current_sample = sample; + } + + this.isirq = function() { + return (AMIGA.INTREQR() & (0x80 << this.num)) != 0; + } + + this.setirq = function(which) { + //if (debugchannel(this.num) && this.wlen > 1) BUG.info('Audio.channel[%d].setirq() %d, %d', this.num, which, this.isirq() ? 1 : 0); + AMIGA.INTREQ_0(INTF_SETCLR | (0x80 << this.num)); + } + + this.zerostate = function() { + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].zerostate()', this.num); + this.state = 0; + this.evtime = CYCLE_MAX; + this.intreq = false; + this.dmaenstore = false; + } + + this.setdr = function() { + //if (debugchannel(this.num) && this.dr) BUG.info('Audio.channel[%d].setdr() DR already active (STATE %d)', this.num, this.state); + this.dr = true; + if (this.wlen == 1) { + this.dsr = true; + //if (debugchannel(this.num) && this.wlen > 1) BUG.info('Audio.channel[%d].setdr() DSR on, pt %08x', this.num, this.pt); + } + } + + this.loaddat = function(modper) { + var audav = (AMIGA.adkcon & (0x01 << this.num)) != 0; + var audap = (AMIGA.adkcon & (0x10 << this.num)) != 0; + if (audav || (modper && audap)) { + if (this.num >= 3) + return; + if (modper && audap) { + if (this.dat == 0) + AMIGA.audio.channel[this.num + 1].per = PERIOD_MAX; + else if (this.dat > PERIOD_MIN) + AMIGA.audio.channel[this.num + 1].per = this.dat * CYCLE_UNIT; + else + AMIGA.audio.channel[this.num + 1].per = PERIOD_MIN * CYCLE_UNIT; + } else if (audav) { + AMIGA.audio.channel[this.num + 1].vol = this.dat; + AMIGA.audio.channel[this.num + 1].vol &= 127; + if (AMIGA.audio.channel[this.num + 1].vol > 64) + AMIGA.audio.channel[this.num + 1].vol = 64; + } + } else { + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].loaddat() new %04x, old %04x', this.num, this.dat, this.dat2); + this.dat2 = this.dat; + } + } + + this.loadper = function() { + this.evtime = this.per; + if (this.evtime < CYCLE_UNIT) + BUG.info('Audio.channel[%d].loadper() bug %d', this.num, this.evtime); + } + + this.state_channel = function(perfin) { + this.state_channel2(perfin); + this.dat_written = false; + } + + this.state_channel2 = function(perfin) { + var chan_ena = ((AMIGA.dmacon & DMAF_DMAEN) && (AMIGA.dmacon & (1 << this.num))) ? true : false; + var old_dma = this.dmaenstore; + var audav = (AMIGA.adkcon & (0x01 << this.num)) != 0; + var audap = (AMIGA.adkcon & (0x10 << this.num)) != 0; + var napnav = (!audav && !audap) || audav; + this.dmaenstore = chan_ena; + + if (!AMIGA.config.audio.enabled) { + this.zerostate(); + return; + } + AMIGA.audio.activate(); + + if ((this.state == 2 || this.state == 3) && AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum && !chan_ena && old_dma) { + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].state_channel2() INSTADMAOFF', this.num); + this.newsample(this.dat2 & 0xff); + if (napnav) + this.setirq(91); + this.zerostate(); + return; + } + + //if (debugchannel(this.num) && old_dma != chan_ena) BUG.info('Audio.channel[%d].state_channel2() DMA %d, IRQ %d', this.num, chan_ena ? 1 : 0, this.isirq() ? 1 : 0); + + switch (this.state) { + case 0: { + if (chan_ena) { + this.evtime = CYCLE_MAX; + this.state = 1; + this.dr = true; + this.wlen = this.len; + this.ptx_written = false; + if (this.wlen > 2) + this.ptx_tofetch = true; + this.dsr = true; + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].state_channel2() 0>1, LEN %d', this.num, this.wlen); + } else if (this.dat_written && !this.isirq()) { + this.state = 2; + this.setirq(0); + this.loaddat(false); + if (AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum && this.per < 10 * CYCLE_UNIT) { + this.newsample(this.dat2 & 0xff); + this.zerostate(); + } else { + this.pbufldl = true; + this.state_channel2(false); + } + } else { + this.zerostate(); + } + break; + } + case 1: { + this.evtime = CYCLE_MAX; + if (!chan_ena) { + this.zerostate(); + return; + } + if (!this.dat_written) + return; + this.setirq(10); + this.setdr(); + if (this.wlen != 1) { + //this.wlen = (this.wlen - 1) & 0xffff; + if ((--this.wlen) < 0) this.wlen = 0xffff; + } + this.state = 5; + break; + } + case 5: { + this.evtime = CYCLE_MAX; + if (!chan_ena) { + this.zerostate(); + return; + } + if (!this.dat_written) + return; + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].state_channel2() >5, LEN %d', this.num, this.wlen); + if (this.ptx_written) { + this.ptx_written = false; + this.lc = this.ptx; + } + this.loaddat(false); + if (napnav) + this.setdr(); + this.state = 2; + this.loadper(); + this.pbufldl = true; + this.intreq = false; + this.state_channel2(false); + break; + } + case 2: { + if (this.pbufldl) { + this.newsample((this.dat2 >> 8) & 0xff); + this.loadper(); + this.pbufldl = false; + } + if (!perfin) + return; + if (audap) + this.loaddat(true); + if (chan_ena) { + if (audap) + this.setdr(); + if (this.intreq && audap) + this.setirq(21); + } else { + if (audap) + this.setirq(22); + } + this.pbufldl = true; + this.state = 3; + this.state_channel2(false); + break; + } + case 3: { + if (this.pbufldl) { + this.newsample((this.dat2 >> 0) & 0xff); + this.loadper(); + this.pbufldl = false; + } + if (!perfin) + return; + if (chan_ena) { + this.loaddat(false); + if (this.intreq && napnav) + this.setirq(31); + if (napnav) + this.setdr(); + } else { + if (this.isirq()) { + //if (debugchannel(this.num)) BUG.info('Audio.channel[%d].state_channel2() IDLE', this.num); + this.zerostate(); + return; + } + this.loaddat(false); + if (napnav) + this.setirq(32); + } + this.intreq = false; + this.pbufldl = true; + this.state = 2; + this.state_channel2(false); + break; + } + } + } +} + +function Audi0() { + var channel = null; + + var last_cycles = 0; + var next_sample_evtime = 0; + var scaled_sample_evtime_orig = 0; + var scaled_sample_evtime = 0; + + var work_to_do = 0; + var prevcon = -1; + + var driver = { + ctx: null, + moz: { + /*currentWritePosition: 0, + prebufferSize: 0, + tail: null, + tailPosition: null,*/ + interval: null + }, + def: { + node: null + }, + paused:false + }; + var sampleBuffer = { + size: 0, + realSize: 0, + data:0, + pos: 0, + rate: new mavg(10) + }; + var ringBuffer = { + size: 0, + data:0, + usage:0, + readPos: 0, + writePos: 0 + }; + + this.filter = new Filter(); + + /*---------------------------------*/ + + function getRate(v) { + switch (v) { + case SAEV_Config_Audio_Rate_11025: return 11025; + case SAEV_Config_Audio_Rate_22050: return 22050; + case SAEV_Config_Audio_Rate_44100: return 44100; + case SAEV_Config_Audio_Rate_48000: return 48000; + default: return 44100; + } + } + + this.recalc_sample_evtime = function() { + var hpos, lines = 0; + + if (AMIGA.config.video.ntsc) { + hpos = 227.5; + lines += 0.5; + } else { + var longframe = (AMIGA.playfield.bplcon0 & 4) ? -1 : AMIGA.events.lofStore; + hpos = 227; + if (longframe < 0) + lines += 0.5; + else if (longframe > 0) + lines += 1.0; + } + lines += AMIGA.events.maxvpos; + + scaled_sample_evtime_orig = Math.floor(hpos * lines * AMIGA.events.hz * CYCLE_UNIT / getRate(AMIGA.config.audio.rate)); + scaled_sample_evtime = scaled_sample_evtime_orig; + //sampler_evtime = hpos * lines * AMIGA.events.hz * CYCLE_UNIT; + + //scaled_sample_evtime = Math.floor((AMIGA.events.maxhpos * AMIGA.events.maxvpos * AMIGA.events.hz + getRate(AMIGA.config.audio.rate) - 1) / getRate(AMIGA.config.audio.rate)) * CYCLE_UNIT; + + console.log('Audio.recalc_sample_evtime() scaled_sample_evtime %f (%f) | hpos %d, lines %d', scaled_sample_evtime, scaled_sample_evtime * CYCLE_UNIT_INV, hpos, lines); + } + + this.setup = function() { + if (channel === null) { + channel = []; + for (var i = 0; i < 4; i++) + channel[i] = new Channel(i); + } + if (!AMIGA.config.audio.enabled || AMIGA.config.audio.mode == SAEV_Config_Audio_Mode_Emul) + return; + + if (driver.ctx === null) { + if (BrowserDetect.browser == 'Firefox') { + try { + driver.ctx = new Audio(); + BUG.info('Audio.setup() using "Audio()"'); + } catch (e) {} + } else { + try { + driver.ctx = new AudioContext(); + BUG.info('Audio.setup() using "AudioContext()"'); + } catch (e) { + try { + driver.ctx = new webkitAudioContext(); + BUG.info('Audio.setup() using "webkitAudioContext()"'); + } catch (e) {} + } + } + } + if (driver.ctx === null) { + if (confirm('Can\'t initialise WebAudio. Continue without audio-playback?')) { + AMIGA.config.audio.mode = SAEV_Config_Audio_Mode_Emul; + return; + } else + Fatal(SAEE_Audio_WebAudio_Not_Avail, 'Can\'t initialise WebAudio.'); + } + + this.filter.setup(0); + + this.recalc_sample_evtime(); + + sampleBuffer.size = 8192 >> 0; + sampleBuffer.realSize = sampleBuffer.size << 1; + sampleBuffer.data = new Float32Array(sampleBuffer.realSize << (AMIGA.config.audio.channels - 1)); + + ringBuffer.size = sampleBuffer.size << 1; + ringBuffer.data = new Float32Array(ringBuffer.size << (AMIGA.config.audio.channels - 1)); + + if (BrowserDetect.browser == 'Firefox') { + driver.ctx.mozSetup(AMIGA.config.audio.channels, getRate(AMIGA.config.audio.rate)); + + /*driver.moz.currentWritePosition = 0; + driver.moz.prebufferSize = getRate(AMIGA.config.audio.rate) >> 1; //prebuffer 500ms + driver.moz.tail = null;*/ + //driver.moz.interval = setInterval(mozHandler, Math.floor(1000 / (getRate(AMIGA.config.audio.rate) / sampleBuffer.size))); + driver.moz.interval = setInterval(mozHandler, Math.floor(1000 / AMIGA.events.hz * (sampleBuffer.size / (getRate(AMIGA.config.audio.rate) / AMIGA.events.hz)))); + + BUG.info('Audio.setup() enabled mozilla audio, channels %d, rate %d', AMIGA.config.audio.channels, getRate(AMIGA.config.audio.rate)); + } else { + var inputs = BrowserDetect.browser == 'Safari' ? 1 : 0; + //if (inputs == 1) BUG.info('Audio.setup() Safari fix enabled.'); + + driver.def.node = driver.ctx.createJavaScriptNode(sampleBuffer.size, inputs, AMIGA.config.audio.channels); + driver.def.node.onaudioprocess = defFill; + driver.def.node.connect(driver.ctx.destination); + BUG.info('Audio.setup() enabled webkit audio, channels %d, rate %d', AMIGA.config.audio.channels, getRate(AMIGA.config.audio.rate)); + } + } + + this.cleanup = function() { + if (driver.ctx !== null) { + if (BrowserDetect.browser == 'Firefox') { + clearInterval(driver.moz.interval); + } else { + driver.def.node.disconnect(0); + driver.def.node = null; + } + } + } + + this.pauseResume = function(pause) { + if (!AMIGA.config.audio.enabled || AMIGA.config.audio.mode == SAEV_Config_Audio_Mode_Emul) return; + + if (driver.ctx !== null) { + if (BrowserDetect.browser == 'Firefox') { + if (pause && !driver.paused) { + clearInterval(driver.moz.interval); + driver.paused = true; + } else if (!pause && driver.paused) { + driver.moz.interval = setInterval(mozHandler, 100); + driver.paused = false; + } + } else { + if (driver.def.node) { + if (pause && !driver.paused) { + driver.def.node.disconnect(0); + driver.paused = true; + } else if (!pause && driver.paused) { + driver.def.node.connect(driver.ctx.destination); + driver.paused = false; + } + } + } + } + } + + this.reset = function() { + for (var i = 0; i < 4; i++) + channel[i].reset(); + + last_cycles = AMIGA.events.currcycle; + next_sample_evtime = scaled_sample_evtime; + this.schedule(); + AMIGA.events.schedule(); + + work_to_do = 0; + prevcon = -1; + + sampleBuffer.pos = 0; + sampleBuffer.rate.clr(); + + ringBuffer.usage = 0; + ringBuffer.readPos = 0; + ringBuffer.writePos = 0; + + this.filter.reset(); + } + + /*---------------------------------*/ + + this.event_reset = function() { + for (var i = 0; i < 4; i++) + channel[i].zerostate(); + + last_cycles = AMIGA.events.currcycle; + next_sample_evtime = scaled_sample_evtime; + this.schedule(); + AMIGA.events.schedule(); + } + + this.activate = function() { + //BUG.info('Audio.activate()'); + var ret = 0; + + if (!work_to_do) { + this.pauseResume(0); + ret = 1; + this.event_reset(); + } + work_to_do = 4 * AMIGA.events.maxvpos * 50; + return ret; + } + + this.deactivate = function() { + //BUG.info('Audio.deactivate()'); + this.pauseResume(1); + sampleBuffer.pos = 0; + sampleBuffer.rate.clr(); + this.event_reset(); + } + + this.state_machine = function() { + this.update(); + for (var i = 0; i < 4; i++) + channel[i].state_channel(false); + + this.schedule(); + AMIGA.events.schedule(); + } + + this.schedule = function() { + var best = CYCLE_MAX; + + AMIGA.events.eventtab[EV_AUDIO].active = false; + AMIGA.events.eventtab[EV_AUDIO].oldcycles = AMIGA.events.currcycle; + + for (var i = 0; i < 4; i++) { + if (channel[i].evtime != CYCLE_MAX) { + if (best > channel[i].evtime) { + best = channel[i].evtime; + AMIGA.events.eventtab[EV_AUDIO].active = true; + } + } + } + AMIGA.events.eventtab[EV_AUDIO].evtime = AMIGA.events.currcycle + best; + } + + this.update = function() { + if (!AMIGA.config.audio.enabled || !work_to_do) { + last_cycles = AMIGA.events.currcycle; + return; + } + + var n_cycles = AMIGA.events.currcycle - last_cycles; + while (n_cycles > 0) { + var best_evtime = n_cycles + 1; + var i, rounded; + + for (i = 0; i < 4; i++) { + if (channel[i].evtime != CYCLE_MAX && best_evtime > channel[i].evtime) + best_evtime = channel[i].evtime; + } + + rounded = Math.floor(next_sample_evtime); + if ((next_sample_evtime - rounded) >= 0.5) + rounded++; + + if (AMIGA.config.audio.mode != SAEV_Config_Audio_Mode_Emul && best_evtime > rounded) + best_evtime = rounded; + + if (best_evtime > n_cycles) + best_evtime = n_cycles; + + next_sample_evtime -= best_evtime; + + /*if (AMIGA.config.audio.mode != SAEV_Config_Audio_Mode_Emul) { + if (sample_prehandler) + sample_prehandler (best_evtime / CYCLE_UNIT); + }*/ + + for (i = 0; i < 4; i++) { + if (channel[i].evtime != CYCLE_MAX) + channel[i].evtime -= best_evtime; + } + n_cycles -= best_evtime; + + if (AMIGA.config.audio.mode != SAEV_Config_Audio_Mode_Emul) { + if (rounded == best_evtime) { + next_sample_evtime += scaled_sample_evtime; + + this.sample_handler_def(); + //this.sample_handler_crux(); + //this.sample_handler_rh(); + } + } + + for (i = 0; i < 4; i++) { + if (channel[i].evtime == 0) { + channel[i].state_channel(true); + if (channel[i].evtime == 0) { + BUG.info('Audio.update() sound bug in channel %d (evtime == 0)', i); + channel[i].evtime = CYCLE_MAX; + } + } + } + } + last_cycles = AMIGA.events.currcycle - n_cycles; + } + + this.update_adkmasks = function() { + var t = AMIGA.adkcon | (AMIGA.adkcon >> 4); + + channel[0].adk_mask = ((t >> 0) & 1) == 0 ? 0xffffffff : 0; + channel[1].adk_mask = ((t >> 1) & 1) == 0 ? 0xffffffff : 0; + channel[2].adk_mask = ((t >> 2) & 1) == 0 ? 0xffffffff : 0; + channel[3].adk_mask = ((t >> 3) & 1) == 0 ? 0xffffffff : 0; + channel[0].enabled = ((t >> 0) & 1) == 0; + channel[1].enabled = ((t >> 1) & 1) == 0; + channel[2].enabled = ((t >> 2) & 1) == 0; + channel[3].enabled = ((t >> 3) & 1) == 0; + + if ((prevcon & 0xff) != (AMIGA.adkcon & 0xff)) { + this.activate(); + prevcon = AMIGA.adkcon; + } + } + + this.handler = function() { + this.update(); + this.schedule(); + } + + this.hsync = function() { + //if (!AMIGA.config.audio.enabled) return; /* done in events.hSyncPre() */ + + if (work_to_do > 0) { + work_to_do--; + if (work_to_do == 0) + this.deactivate(); + } + this.update(); + } + + /*---------------------------------*/ + + this.AUDxDAT = function(nr, v) { + //BUG.info('AUD%dDAT %x', nr, v); + channel[nr].dat = v; + channel[nr].dat_written = true; + if (channel[nr].state == 2 || channel[nr].state == 3) { + var chan_ena = ((AMIGA.dmacon & DMAF_DMAEN) && (AMIGA.dmacon & (1 << nr))) ? true : false; + if (chan_ena) { + if (channel[nr].wlen == 1) { + channel[nr].wlen = channel[nr].len; + channel[nr].intreq = true; + } else { + //channel[nr].wlen = (channel[nr].wlen - 1) & 0xffff; + if ((--channel[nr].wlen) < 0) channel[nr].wlen = 0xffff; + } + } + } else { + this.activate(); + this.update(); + channel[nr].state_channel(false); + this.schedule(); + AMIGA.events.schedule(); + } + channel[nr].dat_written = false; + } + + this.AUDxPER = function(nr, v) { + this.activate(); + this.update(); + + var per = v * CYCLE_UNIT; + if (per == 0) + per = PERIOD_MAX - 1; + + if (per < PERIOD_MIN * CYCLE_UNIT) + per = PERIOD_MIN * CYCLE_UNIT; + if (per < PERIOD_MIN_NONCE * CYCLE_UNIT && !AMIGA.config.cpu.exact && channel[nr].dmaenstore) + per = PERIOD_MIN_NONCE * CYCLE_UNIT; + + if (channel[nr].per == PERIOD_MAX - 1 && per != PERIOD_MAX - 1) { + channel[nr].evtime = CYCLE_UNIT; + if (AMIGA.config.audio.enabled) { + this.schedule(); + AMIGA.events.schedule(); + } + } + channel[nr].per = per; + //if (debugchannel(nr)) BUG.info('AUD%dPER() %x', nr, v); + } + + this.AUDxLEN = function(nr, v) { + this.activate(); + this.update(); + channel[nr].len = v; + //if (debugchannel(nr)) BUG.info('AUD%dLEN() %x', nr, v); + } + + this.AUDxVOL = function(nr, v) { + v &= 127; + if (v > 64) v = 64; + this.activate(); + this.update(); + channel[nr].vol = v; + //if (debugchannel(nr)) BUG.info('AUD%dVOL() %x', nr, v); + } + + this.AUDxLCH = function(nr, v) { + this.activate(); + this.update(); + + if (AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum && ((channel[nr].ptx_tofetch && channel[nr].state == 1) || channel[nr].ptx_written)) { + channel[nr].ptx = channel[nr].lc; + channel[nr].ptx_written = true; + } else + channel[nr].lc = ((channel[nr].lc & 0xffff) | (v << 16)) >>> 0; + } + + this.AUDxLCL = function(nr, v) { + this.activate(); + this.update(); + + if (AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum && ((channel[nr].ptx_tofetch && channel[nr].state == 1) || channel[nr].ptx_written)) { + channel[nr].ptx = channel[nr].lc; + channel[nr].ptx_written = true; + } else + channel[nr].lc = ((channel[nr].lc & ~0xffff) | (v & 0xfffe)) >>> 0; + } + + /*---------------------------------*/ + + this.getpt = function(nr, reset) { + var p = channel[nr].pt; + channel[nr].pt += 2; + if (reset) + channel[nr].pt = channel[nr].lc; + channel[nr].ptx_tofetch = false; + return p; + } + + this.dmal = function() { + var dmal = 0; + for (var nr = 0; nr < 4; nr++) { + if (channel[nr].dr) + dmal |= (1 << (nr * 2)); + if (channel[nr].dsr) + dmal |= (1 << (nr * 2 + 1)); + channel[nr].dr = channel[nr].dsr = false; + } + //if (dmal) BUG.info('Audio.dmal() %d', dmal); + return dmal; + } + + /*---------------------------------*/ + + const inv32768 = 1.0 / 32768; + + this.sample_handler_def = function() { + var data0 = channel[0].enabled ? (channel[0].current_sample * channel[0].vol) : 0; + var data1 = channel[1].enabled ? (channel[1].current_sample * channel[1].vol) : 0; + var data2 = channel[2].enabled ? (channel[2].current_sample * channel[2].vol) : 0; + var data3 = channel[3].enabled ? (channel[3].current_sample * channel[3].vol) : 0; + + data0 += data3; + data1 += data2; + data2 = data0 << 1; + data3 = data1 << 1; + data2 = this.filter.filter(data2, 0); + data3 = this.filter.filter(data3, 1); + + if (sampleBuffer.pos < sampleBuffer.realSize) { + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + sampleBuffer.data[(sampleBuffer.pos << 1) ] = inv32768 * data2; + sampleBuffer.data[(sampleBuffer.pos << 1) + 1] = inv32768 * data3; + sampleBuffer.pos++; + } else + sampleBuffer.data[sampleBuffer.pos++] = inv32768 * ((data2 + data3) * 0.5); + } else + BUG.info('Audio.sample_handler() audio buffer over-run!'); + } + + this.sample_handler_crux = function() { + var data0 = channel[0].enabled ? (channel[0].current_sample * channel[0].vol) : 0; + var data1 = channel[1].enabled ? (channel[1].current_sample * channel[1].vol) : 0; + var data2 = channel[2].enabled ? (channel[2].current_sample * channel[2].vol) : 0; + var data3 = channel[3].enabled ? (channel[3].current_sample * channel[3].vol) : 0; + + var data0p = channel[0].enabled ? (channel[0].last_sample * channel[0].vol) : 0; + var data1p = channel[1].enabled ? (channel[1].last_sample * channel[1].vol) : 0; + var data2p = channel[2].enabled ? (channel[2].last_sample * channel[2].vol) : 0; + var data3p = channel[3].enabled ? (channel[3].last_sample * channel[3].vol) : 0; + + { + const INTERVAL = scaled_sample_evtime * 3; + var ratio, ratio1; + + ratio1 = channel[0].per - channel[0].evtime; + ratio = Math.floor((ratio1 << 12) / INTERVAL); + if (channel[0].evtime < scaled_sample_evtime || ratio1 >= INTERVAL) + ratio = 4096; + data0 = (data0 * ratio + data0p * (4096 - ratio)) >> 12; + + ratio1 = channel[1].per - channel[1].evtime; + ratio = Math.floor((ratio1 << 12) / INTERVAL); + if (channel[1].evtime < scaled_sample_evtime || ratio1 >= INTERVAL) + ratio = 4096; + data1 = (data1 * ratio + data1p * (4096 - ratio)) >> 12; + + ratio1 = channel[2].per - channel[2].evtime; + ratio = Math.floor((ratio1 << 12) / INTERVAL); + if (channel[2].evtime < scaled_sample_evtime || ratio1 >= INTERVAL) + ratio = 4096; + data2 = (data2 * ratio + data2p * (4096 - ratio)) >> 12; + + ratio1 = channel[3].per - channel[3].evtime; + ratio = Math.floor((ratio1 << 12) / INTERVAL); + if (channel[3].evtime < scaled_sample_evtime || ratio1 >= INTERVAL) + ratio = 4096; + data3 = (data3 * ratio + data3p * (4096 - ratio)) >> 12; + } + data0 += data3; + data1 += data2; + data2 = data0 << 1; + data3 = data1 << 1; + data2 = this.filter.filter(data2, 0); + data3 = this.filter.filter(data3, 1); + + if (sampleBuffer.pos < sampleBuffer.realSize) { + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + sampleBuffer.data[(sampleBuffer.pos << 1) ] = inv32768 * data2; + sampleBuffer.data[(sampleBuffer.pos << 1) + 1] = inv32768 * data3; + sampleBuffer.pos++; + } else + sampleBuffer.data[sampleBuffer.pos++] = inv32768 * ((data2 + data3) * 0.5); + } else + BUG.info('Audio.sample_handler() audio buffer over-run!'); + } + + this.sample_handler_rh = function() { + var data0 = channel[0].enabled ? (channel[0].current_sample * channel[0].vol) : 0; + var data1 = channel[1].enabled ? (channel[1].current_sample * channel[1].vol) : 0; + var data2 = channel[2].enabled ? (channel[2].current_sample * channel[2].vol) : 0; + var data3 = channel[3].enabled ? (channel[3].current_sample * channel[3].vol) : 0; + var data0p = channel[0].enabled ? (channel[0].last_sample * channel[0].vol) : 0; + var data1p = channel[1].enabled ? (channel[1].last_sample * channel[1].vol) : 0; + var data2p = channel[2].enabled ? (channel[2].last_sample * channel[2].vol) : 0; + var data3p = channel[3].enabled ? (channel[3].last_sample * channel[3].vol) : 0; + + { + var delta, ratio; + + delta = channel[0].per; + ratio = ((channel[0].evtime % delta) << 8) / delta; + data0 = (data0 * (256 - ratio) + data0p * ratio) >> 8; + delta = channel[1].per; + ratio = ((channel[1].evtime % delta) << 8) / delta; + data1 = (data1 * (256 - ratio) + data1p * ratio) >> 8; + delta = channel[2].per; + ratio = ((channel[2].evtime % delta) << 8) / delta; + data1 += (data2 * (256 - ratio) + data2p * ratio) >> 8; + delta = channel[3].per; + ratio = ((channel[3].evtime % delta) << 8) / delta; + data0 += (data3 * (256 - ratio) + data3p * ratio) >> 8; + } + + data2 = data0 << 1; + data3 = data1 << 1; + data2 = this.filter.filter(data2, 0); + data3 = this.filter.filter(data3, 1); + + if (sampleBuffer.pos < sampleBuffer.realSize) { + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + sampleBuffer.data[(sampleBuffer.pos << 1) ] = inv32768 * data2; + sampleBuffer.data[(sampleBuffer.pos << 1) + 1] = inv32768 * data3; + sampleBuffer.pos++; + } else + sampleBuffer.data[sampleBuffer.pos++] = inv32768 * ((data2 + data3) * 0.5); + } else + BUG.info('Audio.sample_handler() audio buffer over-run!'); + } + + /*---------------------------------*/ + + //var avg = 0, cnt = 0; + + function fillRingBuffer() { + if (sampleBuffer.pos == 0) return false; + + /*avg += sampleBuffer.pos; + if ((++cnt) == 7) { + var newrate = Math.floor(avg / 7); + sampleBuffer.rate = Math.floor(sampleBuffer.rate + newrate >> 1); + avg = cnt = 0; + } + if (sampleBuffer.rate == 0) { + sampleBuffer.pos = 0; + return false; + } + var rate = sampleBuffer.rate;*/ + var rate = sampleBuffer.rate.set(sampleBuffer.pos); + var drop = false; + + if (ringBuffer.usage + sampleBuffer.pos < ringBuffer.size) { + for (var i = 0; i < sampleBuffer.pos; i++) { + var pos = ringBuffer.writePos + i < ringBuffer.size ? ringBuffer.writePos + i : ringBuffer.writePos + i - ringBuffer.size; + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + ringBuffer.data[(pos << 1) ] = sampleBuffer.data[(i << 1) ]; + ringBuffer.data[(pos << 1) + 1] = sampleBuffer.data[(i << 1) + 1]; + } else + ringBuffer.data[pos] = sampleBuffer.data[i]; + } + ringBuffer.writePos += sampleBuffer.pos; if (ringBuffer.writePos >= ringBuffer.size) ringBuffer.writePos -= ringBuffer.size; + ringBuffer.usage += sampleBuffer.pos; + } else { + //console.log('ringBuffer overflow', ringBuffer.usage, rate, ringBuffer.size - ringBuffer.usage); + + /* fill rest until full */ + var rest = ringBuffer.size - ringBuffer.usage; + for (var i = 0; i < rest; i++) { + var pos = ringBuffer.writePos + i < ringBuffer.size ? ringBuffer.writePos + i : ringBuffer.writePos + i - ringBuffer.size; + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + ringBuffer.data[(pos << 1) ] = sampleBuffer.data[(i << 1) ]; + ringBuffer.data[(pos << 1) + 1] = sampleBuffer.data[(i << 1) + 1]; + } else + ringBuffer.data[pos] = sampleBuffer.data[i]; + } + ringBuffer.writePos += rest; if (ringBuffer.writePos >= ringBuffer.size) ringBuffer.writePos -= ringBuffer.size; + ringBuffer.usage += rest; + + rate = sampleBuffer.size;// + (sampleBuffer.size >> 1); + drop = true; + } + if (ringBuffer.usage - rate < 0) { + //console.log('ringBuffer underflow', ringBuffer.usage, rate); + rate += ringBuffer.usage - rate; + drop = false; + } + + var playBuffer = new Float32Array(rate << (AMIGA.config.audio.channels - 1)); + if (ringBuffer.usage - rate >= 0) { + for (var i = 0; i < rate; i++) { + var pos = ringBuffer.readPos + i < ringBuffer.size ? ringBuffer.readPos + i : ringBuffer.readPos + i - ringBuffer.size; + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + playBuffer[(i << 1) ] = ringBuffer.data[(pos << 1) ]; + playBuffer[(i << 1) + 1] = ringBuffer.data[(pos << 1) + 1]; + } else + playBuffer[i] = ringBuffer.data[pos]; + } + ringBuffer.readPos += rate; if (ringBuffer.readPos >= ringBuffer.size) ringBuffer.readPos -= ringBuffer.size; + ringBuffer.usage -= rate; + + if (drop) { + ringBuffer.readPos += ringBuffer.usage; if (ringBuffer.readPos >= ringBuffer.size) ringBuffer.readPos -= ringBuffer.size; + ringBuffer.usage = 0; + } + } + + //console.log(sampleBuffer.pos, ringBuffer.usage, rate); + sampleBuffer.pos = 0; + + return [playBuffer, rate]; + } + + function defFill(e) { + var rb = fillRingBuffer(); + if (rb === false) return; + var playBuffer = rb[0]; + var rate = rb[1]; + + var step = rate / sampleBuffer.size; + + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + var data1 = e.outputBuffer.getChannelData(0); + var data2 = e.outputBuffer.getChannelData(1); + + for (var i = 0, j = 0.0; i < sampleBuffer.size; i++, j += step) { + data1[i] = playBuffer[(j >> 0) << 1]; + data2[i] = playBuffer[((j >> 0) << 1) + 1]; + } + } else { + var data = e.outputBuffer.getChannelData(0); + + for (var i = 0, j = 0.0; i < sampleBuffer.size; i++, j += step) + data[i] = playBuffer[j >> 0]; + } + } + + function mozFill(data) { + var rb = fillRingBuffer(); + if (rb === false) return; + var playBuffer = rb[0]; + var rate = rb[1]; + + var step = rate / sampleBuffer.size; + + if (AMIGA.config.audio.channels == SAEV_Config_Audio_Channels_Stereo) { + for (var i = 0, j = 0.0; i < sampleBuffer.size; i++, j += step) { + data[i*2] = playBuffer[(j >> 0)*2]; + data[i*2+1] = playBuffer[(j >> 0)*2+1]; + } + } else { + for (var i = 0, j = 0.0; i < data.length; i++, j += step) + data[i] = playBuffer[j >> 0]; + } + } + + function mozHandler() { + var soundData = new Float32Array(sampleBuffer.realSize); + mozFill(soundData); + driver.ctx.mozWriteAudio(soundData); + } + + function mozHandler_full() { + var written; + + if (driver.moz.tail) { + written = driver.ctx.mozWriteAudio(driver.moz.tail.subarray(driver.moz.tailPosition)); + driver.moz.currentWritePosition += written; + driver.moz.tailPosition += written; + if (driver.moz.tailPosition < driver.moz.tail.length) return; + driver.moz.tail = null; + } + + var currentPosition = driver.ctx.mozCurrentSampleOffset(); + var available = currentPosition + driver.moz.prebufferSize - driver.moz.currentWritePosition; + if (available > 0) { + var soundData = new Float32Array(available); + mozFill(soundData); + + written = driver.ctx.mozWriteAudio(soundData); + if (written < soundData.length) { + driver.moz.tail = soundData; + driver.moz.tailPosition = written; + } + driver.moz.currentWritePosition += written; + } + } +} diff --git a/sae/blitter.js b/sae/blitter.js new file mode 100644 index 0000000..e34b4a9 --- /dev/null +++ b/sae/blitter.js @@ -0,0 +1,2904 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Blitter() { + const BLITTER_MAX_WORDS = 2048; + + /*const BLITTER_STARTUP_CYCLES = 2; + const BLIT_MODE_IMMEDIATE = -1; + const BLIT_MODE_APPROXIMATE = 0; + const BLIT_MODE_COMPATIBLE = 1; + const BLIT_MODE_EXACT = 2;*/ + + var blit_filltable = []; + var blit_masktable = []; + + var bltcon0 = 0; + var bltcon1 = 0; + var bltapt = 0; + var bltapt_line = 0; + var bltbpt = 0; + var bltcpt = 0; + var bltdpt = 0; + + var blinea_shift = 0; + var blinea = 0, blineb = 0; + var blitline = 0, blitfc = 0, blitfill = 0, blitife = 0, blitsing = 0, blitdesc = 0; + var blitonedot = 0, blitsign = 0, blitlinepixel = 0; + //var blit_add = 0; + //var blit_modadda = 0, blit_modaddb = 0, blit_modaddc = 0, blit_modaddd = 0; + var blit_ch = 0; + + var blt_info = { + blitzero:0, + blitashift:0, blitbshift:0, blitdownashift:0, blitdownbshift:0, + bltadat:0, bltbdat:0, bltcdat:0, bltddat:0, + bltahold:0, bltbhold:0, bltafwm:0, bltalwm:0, + vblitsize:0, hblitsize:0, + bltamod:0, bltbmod:0, bltcmod:0, bltdmod:0, + got_cycle:0 + }; + + var bltstate = BLT_done; + + var original_ch = 0, original_fill = 0, original_line = 0; + var blit_interrupt = true; + var blitter_cycle_exact = false; + + var blit_cyclecounter = 0; + //var blit_waitcyclecounter = 0; + //var blit_maxcyclecounter = 0; + //var blit_totalcyclecounter = 0; + //var blit_startcycles = 0; + //var blit_misscyclecounter = 0; + var blit_slowdown = 0; + //var last_blitter_hpos = 0; + var blit_stuck = 0; + + var blit_firstline_cycles = 0; + var blit_first_cycle = 0; + var blit_last_cycle = 0, blit_dmacount = 0, blit_dmacount2 = 0; + //var blit_linecycles = 0, blit_extracycles = 0 + var blit_nod = 0; + var blit_diag = []; + //var blit_frozen = 0; + var blit_faulty = 0; + var blit_final = 0; + //var blt_delayed_irq = 0; + var ddat1 = 0, ddat2 = 0; + var ddat1use = 0, ddat2use = 0; + + const blit_cycle_diagram = [ + [ 2, 0,0, 0,0 ], /* 0 -- */ + [ 2, 0,0, 0,4 ], /* 1 -D */ + [ 2, 0,3, 0,3 ], /* 2 -C */ + [ 3, 0,3,0, 0,3,4 ], /* 3 -CD */ + [ 3, 0,2,0, 0,2,0 ], /* 4 -B- */ + [ 3, 0,2,0, 0,2,4 ], /* 5 -BD */ + [ 3, 0,2,3, 0,2,3 ], /* 6 -BC */ + [ 4, 0,2,3,0, 0,2,3,4 ], /* 7 -BCD */ + [ 2, 1,0, 1,0 ], /* 8 A- */ + [ 2, 1,0, 1,4 ], /* 9 AD */ + [ 2, 1,3, 1,3 ], /* A AC */ + [ 3, 1,3,0, 1,3,4, ], /* B ACD */ + [ 3, 1,2,0, 1,2,0 ], /* C AB- */ + [ 3, 1,2,0, 1,2,4 ], /* D ABD */ + [ 3, 1,2,3, 1,2,3 ], /* E ABC */ + [ 4, 1,2,3,0, 1,2,3,4 ] /* F ABCD */ + ]; + const blit_cycle_diagram_fill = [ + [ 0 ], /* 0 */ + [ 3, 0,0,0, 0,4,0 ], /* 1 */ + [ 0 ], /* 2 */ + [ 0 ], /* 3 */ + [ 0 ], /* 4 */ + [ 4, 0,2,0,0, 0,2,4,0 ], /* 5 */ + [ 0 ], /* 6 */ + [ 0 ], /* 7 */ + [ 0 ], /* 8 */ + [ 3, 1,0,0, 1,4,0 ], /* 9 */ + [ 0 ], /* A */ + [ 0 ], /* B */ + [ 0 ], /* C */ + [ 4, 1,2,0,0, 1,2,4,0 ], /* D */ + [ 0 ], /* E */ + [ 0 ] /* F */ + ]; + const blit_cycle_diagram_line = [4, 0,3,5,4, 0,3,5,4]; + const blit_cycle_diagram_finald = [2, 0,4, 0,4]; + const blit_cycle_diagram_finalld = [2, 0,0, 0,0]; + + //function build_blitfilltable() + { + var blit_masktable = new Uint16Array(BLITTER_MAX_WORDS); + for (var i = 0; i < BLITTER_MAX_WORDS; i++) + blit_masktable[i] = 0xffff; + + blit_filltable = []; + for (var d = 0; d < 256; d++) { + blit_filltable[d] = []; + for (var i = 0; i < 4; i++) { + var fc = (i & 1) == 1; + var data = d; + blit_filltable[d][i] = []; + for (var fillmask = 1; fillmask != 0x100; fillmask <<= 1) { + var tmp = data; + if (fc) { + if (i & 2) + data |= fillmask; + else + data ^= fillmask; + } + if (tmp & fillmask) fc = !fc; + } + blit_filltable[d][i][0] = data; + blit_filltable[d][i][1] = fc; + } + } + } + + /*---------------------------------*/ + + this.reset = function() { + bltstate = BLT_done; + blit_interrupt = true; + blit_stuck = 0; + } + + function blitter_dump() { + BUG.info('PT A=%08X B=%08X C=%08X D=%08X', bltapt, bltbpt, bltcpt, bltdpt); + BUG.info('CON0=%04X CON1=%04X DAT A=%04X B=%04X C=%04X', bltcon0, bltcon1, blt_info.bltadat, blt_info.bltbdat, blt_info.bltcdat); + //BUG.info('AFWM=%04X ALWM=%04X MOD A=%04X B=%04X C=%04X D=%04X', blt_info.bltafwm, blt_info.bltalwm, blt_info.bltamod & 0xffff, blt_info.bltbmod & 0xffff, blt_info.bltcmod & 0xffff, blt_info.bltdmod & 0xffff); + BUG.info('AFWM=%04X ALWM=%04X MOD A=%04X B=%04X C=%04X D=%04X', blt_info.bltafwm, blt_info.bltalwm, blt_info.bltamod, blt_info.bltbmod, blt_info.bltcmod, blt_info.bltdmod); + } + + function castWord(v) { + return (v & 0x8000) ? (v - 0x10000) : v; + } + + function get_ch() { + if (blit_faulty) { + console.log('get_ch() blit_faulty'); + //FIXME + return blit_cycle_diagram[0]; //&blit_diag[0]; + } + if (blit_final) + return blitline ? blit_cycle_diagram_finalld : blit_cycle_diagram_finald; + return blit_diag; + } + + function channel_state(cycles) { + //console.log('channel_state()', cycles); + if (cycles < 0) + return 0; + var diag = get_ch(); + if (cycles < diag[0]) + return diag[1 + cycles]; + cycles -= diag[0]; + cycles %= diag[0]; + return diag[1 + diag[0] + cycles]; + } + + /*function channel_pos(cycles) { + if (cycles < 0) + return 0; + var diag = get_ch(); + if (cycles < diag[0]) + return cycles; + cycles -= diag[0]; + cycles %= diag[0]; + return cycles; + }*/ + + /*function canblit(hpos) { + if (is_bitplane_dma (hpos)) + return 0; + if (cycle_line[hpos] & CYCLE_MASK) return 0; + return 1; + }*/ + + function blitter_interrupt(hpos, done) { + if (blit_interrupt) + return; + if (!done && (!AMIGA.config.blitter.exact || AMIGA.config.cpu.model >= 68020)) + return; + blit_interrupt = true; + AMIGA.send_interrupt(6, 3 * CYCLE_UNIT); + //if (debug_dma) record_dma_event (DMA_EVENT_BLITIRQ, hpos, vpos); + } + + function blitter_done(hpos) { + ddat1use = ddat2use = 0; + //bltstate = blit_startcycles == 0 || !AMIGA.config.blitter.exact ? BLT_done : BLT_init; + bltstate = !AMIGA.config.blitter.exact ? BLT_done : BLT_init; + blitter_interrupt(hpos, 1); + AMIGA.copper.blitter_done_notify(hpos); + //if (debug_dma) record_dma_event (DMA_EVENT_BLITFINISHED, hpos, vpos); + AMIGA.events.event2_remevent(EV2_BLITTER); + clr_special (SPCFLAG_BLTNASTY); + //BUG.info('blitter_done() cycles %d, missed %d, total %d', blit_totalcyclecounter, blit_misscyclecounter, blit_totalcyclecounter + blit_misscyclecounter); + } + + /*---------------------------------*/ + /* ~1500 lines of auto-generated functions are folling... */ + /*---------------------------------*/ + + function blitdofast_0(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (0) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (ptd) ptd += b.bltdmod; + } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_0(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (0) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (ptd) ptd -= b.bltdmod; + } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((~srca & srcc)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((~srca & srcc)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_2a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc & ~(srca & srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_2a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc & ~(srca & srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_30(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca & ~srcb)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_30(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca & ~srcb)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_3a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcb ^ (srca | (srcb ^ srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_3a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcb ^ (srca | (srcb ^ srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_3c(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca ^ srcb)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_3c(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca ^ srcb)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_4a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & (srcb | srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_4a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & (srcb | srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_6a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_6a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_8a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc & (~srca | srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_8a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc & (~srca | srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_8c(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcb & (~srca | srcc))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_8c(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcb & (~srca | srcc))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_9a(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & ~srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_9a(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & ~srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_a8(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc & (srca | srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_a8(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc & (srca | srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_aa(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (srcc) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_aa(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (srcc) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_b1(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (~(srca ^ (srcc | (srca ^ srcb)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_b1(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (~(srca ^ (srcc | (srca ^ srcb)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_ca(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & (srcb ^ srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_ca(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srca & (srcb ^ srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_cc(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (srcb) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (ptb) ptb += b.bltbmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_cc(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var prevb = 0, srcb = b.bltbhold; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (srcb) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (ptb) ptb -= b.bltbmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_d8(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca ^ (srcc & (srca ^ srcb)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_d8(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca ^ (srcc & (srca ^ srcb)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_e2(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srcb & (srca ^ srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_e2(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc ^ (srcb & (srca ^ srcc)))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_ea(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc | (srca & srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_ea(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srcc | (srca & srcb))) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_f0(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (srca) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptd) ptd += b.bltdmod; + } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_f0(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = (srca) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptd) ptd -= b.bltdmod; + } + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_fa(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var srcc = b.bltcdat; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc += 2; } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca | srcc)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptc) ptc += b.bltcmod; + if (ptd) ptd += b.bltdmod; + } + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_fa(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var srcc = b.bltcdat; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptc) { srcc = AMIGA.mem.load16_chip(ptc); ptc -= 2; } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca | srcc)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptc) ptc -= b.bltcmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltcdat = srcc; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_fc(pta, ptb, ptc, ptd, b) { + var i,j; + var totald = 0; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd=0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb += 2; + srcb = ((((prevb << 16) | bltbdat) >>> 0) >>> b.blitbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta += 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((preva << 16) | bltadat) >>> 0) >>> b.blitashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca | srcb)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd += 2; } + } + if (pta) pta += b.bltamod; + if (ptb) ptb += b.bltbmod; + if (ptd) ptd += b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + function blitdofast_desc_fc(pta, ptb, ptc, ptd, b) { + var totald = 0; + var i,j; + var preva = 0; + var prevb = 0, srcb = b.bltbhold; + var dstd = 0; + var dstp = 0; + for (j = 0; j < b.vblitsize; j++) { + for (i = 0; i < b.hblitsize; i++) { + var bltadat, srca; + if (ptb) { + var bltbdat = blt_info.bltbdat = AMIGA.mem.load16_chip(ptb); ptb -= 2; + srcb = ((((bltbdat << 16) | prevb) >>> 0) >>> b.blitdownbshift) & 0xffff; + prevb = bltbdat; + } + if (pta) { bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(pta); pta -= 2; } else { bltadat = blt_info.bltadat; } + bltadat &= blit_masktable[i]; + srca = ((((bltadat << 16) | preva) >>> 0) >>> b.blitdownashift) & 0xffff; + preva = bltadat; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + dstd = ((srca | srcb)) & 0xffff; + totald |= dstd; + if (ptd) { dstp = ptd; ptd -= 2; } + } + if (pta) pta -= b.bltamod; + if (ptb) ptb -= b.bltbmod; + if (ptd) ptd -= b.bltdmod; + } + b.bltbhold = srcb; + if (dstp) AMIGA.mem.store16_chip(dstp, dstd); + if (totald != 0) b.blitzero = 0; + } + + const blitfunc_dofast = [ + blitdofast_0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_a, 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, blitdofast_2a, 0, 0, 0, 0, 0, + blitdofast_30, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_3a, 0, blitdofast_3c, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_4a, 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, blitdofast_6a, 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, blitdofast_8a, 0, blitdofast_8c, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_9a, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + blitdofast_a8, 0, blitdofast_aa, 0, 0, 0, 0, 0, + 0, blitdofast_b1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_ca, 0, blitdofast_cc, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + blitdofast_d8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_e2, 0, 0, 0, 0, 0, + 0, 0, blitdofast_ea, 0, 0, 0, 0, 0, + blitdofast_f0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_fa, 0, blitdofast_fc, 0, 0, 0 + ]; + + const blitfunc_dofast_desc = [ + blitdofast_desc_0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_a, 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, blitdofast_desc_2a, 0, 0, 0, 0, 0, + blitdofast_desc_30, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_3a, 0, blitdofast_desc_3c, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_4a, 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, blitdofast_desc_6a, 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, blitdofast_desc_8a, 0, blitdofast_desc_8c, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_9a, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + blitdofast_desc_a8, 0, blitdofast_desc_aa, 0, 0, 0, 0, 0, + 0, blitdofast_desc_b1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_ca, 0, blitdofast_desc_cc, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + blitdofast_desc_d8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_e2, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_ea, 0, 0, 0, 0, 0, + blitdofast_desc_f0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, blitdofast_desc_fa, 0, blitdofast_desc_fc, 0, 0, 0 + ]; + + function blit_func(a, b, c, mt) { + switch (mt) { + case 0x00: return 0; + case 0x01: return (~c & ~b & ~a); + case 0x02: return (c & ~b & ~a); + case 0x03: return (~b & ~a); + case 0x04: return (~c & b & ~a); + case 0x05: return (~c & ~a); + case 0x06: return (c & ~b & ~a) | (~c & b & ~a); + case 0x07: return (~b & ~a) | (~c & ~a); + case 0x08: return (c & b & ~a); + case 0x09: return (~c & ~b & ~a) | (c & b & ~a); + case 0x0a: return (c & ~a); + case 0x0b: return (~b & ~a) | (c & ~a); + case 0x0c: return (b & ~a); + case 0x0d: return (~c & ~a) | (b & ~a); + case 0x0e: return (c & ~a) | (b & ~a); + case 0x0f: return (~a); + case 0x10: return (~c & ~b & a); + case 0x11: return (~c & ~b); + case 0x12: return (c & ~b & ~a) | (~c & ~b & a); + case 0x13: return (~b & ~a) | (~c & ~b); + case 0x14: return (~c & b & ~a) | (~c & ~b & a); + case 0x15: return (~c & ~a) | (~c & ~b); + case 0x16: return (c & ~b & ~a) | (~c & b & ~a) | (~c & ~b & a); + case 0x17: return (~b & ~a) | (~c & ~a) | (~c & ~b); + case 0x18: return (c & b & ~a) | (~c & ~b & a); + case 0x19: return (~c & ~b) | (c & b & ~a); + case 0x1a: return (c & ~a) | (~c & ~b & a); + case 0x1b: return (~b & ~a) | (c & ~a) | (~c & ~b); + case 0x1c: return (b & ~a) | (~c & ~b & a); + case 0x1d: return (~c & ~a) | (b & ~a) | (~c & ~b); + case 0x1e: return (c & ~a) | (b & ~a) | (~c & ~b & a); + case 0x1f: return (~a) | (~c & ~b); + case 0x20: return (c & ~b & a); + case 0x21: return (~c & ~b & ~a) | (c & ~b & a); + case 0x22: return (c & ~b); + case 0x23: return (~b & ~a) | (c & ~b); + case 0x24: return (~c & b & ~a) | (c & ~b & a); + case 0x25: return (~c & ~a) | (c & ~b & a); + case 0x26: return (c & ~b) | (~c & b & ~a); + case 0x27: return (~b & ~a) | (~c & ~a) | (c & ~b); + case 0x28: return (c & b & ~a) | (c & ~b & a); + case 0x29: return (~c & ~b & ~a) | (c & b & ~a) | (c & ~b & a); + case 0x2a: return (c & ~a) | (c & ~b); + case 0x2b: return (~b & ~a) | (c & ~a) | (c & ~b); + case 0x2c: return (b & ~a) | (c & ~b & a); + case 0x2d: return (~c & ~a) | (b & ~a) | (c & ~b & a); + case 0x2e: return (c & ~a) | (b & ~a) | (c & ~b); + case 0x2f: return (~a) | (c & ~b); + case 0x30: return (~b & a); + case 0x31: return (~c & ~b) | (~b & a); + case 0x32: return (c & ~b) | (~b & a); + case 0x33: return (~b); + case 0x34: return (~c & b & ~a) | (~b & a); + case 0x35: return (~c & ~a) | (~b & a); + case 0x36: return (c & ~b) | (~c & b & ~a) | (~b & a); + case 0x37: return (~b) | (~c & ~a); + case 0x38: return (c & b & ~a) | (~b & a); + case 0x39: return (~c & ~b) | (c & b & ~a) | (~b & a); + case 0x3a: return (c & ~a) | (~b & a); + case 0x3b: return (~b) | (c & ~a); + case 0x3c: return (b & ~a) | (~b & a); + case 0x3d: return (~c & ~a) | (b & ~a) | (~b & a); + case 0x3e: return (c & ~a) | (b & ~a) | (~b & a); + case 0x3f: return (~a) | (~b); + case 0x40: return (~c & b & a); + case 0x41: return (~c & ~b & ~a) | (~c & b & a); + case 0x42: return (c & ~b & ~a) | (~c & b & a); + case 0x43: return (~b & ~a) | (~c & b & a); + case 0x44: return (~c & b); + case 0x45: return (~c & ~a) | (~c & b); + case 0x46: return (c & ~b & ~a) | (~c & b); + case 0x47: return (~b & ~a) | (~c & ~a) | (~c & b); + case 0x48: return (c & b & ~a) | (~c & b & a); + case 0x49: return (~c & ~b & ~a) | (c & b & ~a) | (~c & b & a); + case 0x4a: return (c & ~a) | (~c & b & a); + case 0x4b: return (~b & ~a) | (c & ~a) | (~c & b & a); + case 0x4c: return (b & ~a) | (~c & b); + case 0x4d: return (~c & ~a) | (b & ~a) | (~c & b); + case 0x4e: return (c & ~a) | (b & ~a) | (~c & b); + case 0x4f: return (~a) | (~c & b); + case 0x50: return (~c & a); + case 0x51: return (~c & ~b) | (~c & a); + case 0x52: return (c & ~b & ~a) | (~c & a); + case 0x53: return (~b & ~a) | (~c & a); + case 0x54: return (~c & b) | (~c & a); + case 0x55: return (~c); + case 0x56: return (c & ~b & ~a) | (~c & b) | (~c & a); + case 0x57: return (~b & ~a) | (~c); + case 0x58: return (c & b & ~a) | (~c & a); + case 0x59: return (~c & ~b) | (c & b & ~a) | (~c & a); + case 0x5a: return (c & ~a) | (~c & a); + case 0x5b: return (~b & ~a) | (c & ~a) | (~c & a); + case 0x5c: return (b & ~a) | (~c & a); + case 0x5d: return (~c) | (b & ~a); + case 0x5e: return (c & ~a) | (b & ~a) | (~c & a); + case 0x5f: return (~a) | (~c); + case 0x60: return (c & ~b & a) | (~c & b & a); + case 0x61: return (~c & ~b & ~a) | (c & ~b & a) | (~c & b & a); + case 0x62: return (c & ~b) | (~c & b & a); + case 0x63: return (~b & ~a) | (c & ~b) | (~c & b & a); + case 0x64: return (~c & b) | (c & ~b & a); + case 0x65: return (~c & ~a) | (c & ~b & a) | (~c & b); + case 0x66: return (c & ~b) | (~c & b); + case 0x67: return (~b & ~a) | (~c & ~a) | (c & ~b) | (~c & b); + case 0x68: return (c & b & ~a) | (c & ~b & a) | (~c & b & a); + case 0x69: return (~c & ~b & ~a) | (c & b & ~a) | (c & ~b & a) | (~c & b & a); + case 0x6a: return (c & ~a) | (c & ~b) | (~c & b & a); + case 0x6b: return (~b & ~a) | (c & ~a) | (c & ~b) | (~c & b & a); + case 0x6c: return (b & ~a) | (c & ~b & a) | (~c & b); + case 0x6d: return (~c & ~a) | (b & ~a) | (c & ~b & a) | (~c & b); + case 0x6e: return (c & ~a) | (b & ~a) | (c & ~b) | (~c & b); + case 0x6f: return (~a) | (c & ~b) | (~c & b); + case 0x70: return (~b & a) | (~c & a); + case 0x71: return (~c & ~b) | (~b & a) | (~c & a); + case 0x72: return (c & ~b) | (~b & a) | (~c & a); + case 0x73: return (~b) | (~c & a); + case 0x74: return (~c & b) | (~b & a); + case 0x75: return (~c) | (~b & a); + case 0x76: return (c & ~b) | (~c & b) | (~b & a); + case 0x77: return (~b) | (~c); + case 0x78: return (c & b & ~a) | (~b & a) | (~c & a); + case 0x79: return (~c & ~b) | (c & b & ~a) | (~b & a) | (~c & a); + case 0x7a: return (c & ~a) | (~b & a) | (~c & a); + case 0x7b: return (~b) | (c & ~a) | (~c & a); + case 0x7c: return (b & ~a) | (~b & a) | (~c & a); + case 0x7d: return (~c) | (b & ~a) | (~b & a); + case 0x7e: return (c & ~a) | (b & ~a) | (~b & a) | (~c & a); + case 0x7f: return (~a) | (~b) | (~c); + case 0x80: return (c & b & a); + case 0x81: return (~c & ~b & ~a) | (c & b & a); + case 0x82: return (c & ~b & ~a) | (c & b & a); + case 0x83: return (~b & ~a) | (c & b & a); + case 0x84: return (~c & b & ~a) | (c & b & a); + case 0x85: return (~c & ~a) | (c & b & a); + case 0x86: return (c & ~b & ~a) | (~c & b & ~a) | (c & b & a); + case 0x87: return (~b & ~a) | (~c & ~a) | (c & b & a); + case 0x88: return (c & b); + case 0x89: return (~c & ~b & ~a) | (c & b); + case 0x8a: return (c & ~a) | (c & b); + case 0x8b: return (~b & ~a) | (c & ~a) | (c & b); + case 0x8c: return (b & ~a) | (c & b); + case 0x8d: return (~c & ~a) | (b & ~a) | (c & b); + case 0x8e: return (c & ~a) | (b & ~a) | (c & b); + case 0x8f: return (~a) | (c & b); + case 0x90: return (~c & ~b & a) | (c & b & a); + case 0x91: return (~c & ~b) | (c & b & a); + case 0x92: return (c & ~b & ~a) | (~c & ~b & a) | (c & b & a); + case 0x93: return (~b & ~a) | (~c & ~b) | (c & b & a); + case 0x94: return (~c & b & ~a) | (~c & ~b & a) | (c & b & a); + case 0x95: return (~c & ~a) | (~c & ~b) | (c & b & a); + case 0x96: return (c & ~b & ~a) | (~c & b & ~a) | (~c & ~b & a) | (c & b & a); + case 0x97: return (~b & ~a) | (~c & ~a) | (~c & ~b) | (c & b & a); + case 0x98: return (c & b) | (~c & ~b & a); + case 0x99: return (~c & ~b) | (c & b); + case 0x9a: return (c & ~a) | (~c & ~b & a) | (c & b); + case 0x9b: return (~b & ~a) | (c & ~a) | (~c & ~b) | (c & b); + case 0x9c: return (b & ~a) | (~c & ~b & a) | (c & b); + case 0x9d: return (~c & ~a) | (b & ~a) | (~c & ~b) | (c & b); + case 0x9e: return (c & ~a) | (b & ~a) | (~c & ~b & a) | (c & b); + case 0x9f: return (~a) | (~c & ~b) | (c & b); + case 0xa0: return (c & a); + case 0xa1: return (~c & ~b & ~a) | (c & a); + case 0xa2: return (c & ~b) | (c & a); + case 0xa3: return (~b & ~a) | (c & a); + case 0xa4: return (~c & b & ~a) | (c & a); + case 0xa5: return (~c & ~a) | (c & a); + case 0xa6: return (c & ~b) | (~c & b & ~a) | (c & a); + case 0xa7: return (~b & ~a) | (~c & ~a) | (c & a); + case 0xa8: return (c & b) | (c & a); + case 0xa9: return (~c & ~b & ~a) | (c & b) | (c & a); + case 0xaa: return (c); + case 0xab: return (~b & ~a) | (c); + case 0xac: return (b & ~a) | (c & a); + case 0xad: return (~c & ~a) | (b & ~a) | (c & a); + case 0xae: return (c) | (b & ~a); + case 0xaf: return (~a) | (c); + case 0xb0: return (~b & a) | (c & a); + case 0xb1: return (~c & ~b) | (~b & a) | (c & a); + case 0xb2: return (c & ~b) | (~b & a) | (c & a); + case 0xb3: return (~b) | (c & a); + case 0xb4: return (~c & b & ~a) | (~b & a) | (c & a); + case 0xb5: return (~c & ~a) | (~b & a) | (c & a); + case 0xb6: return (c & ~b) | (~c & b & ~a) | (~b & a) | (c & a); + case 0xb7: return (~b) | (~c & ~a) | (c & a); + case 0xb8: return (c & b) | (~b & a); + case 0xb9: return (~c & ~b) | (c & b) | (~b & a); + case 0xba: return (c) | (~b & a); + case 0xbb: return (~b) | (c); + case 0xbc: return (b & ~a) | (~b & a) | (c & a); + case 0xbd: return (~c & ~a) | (b & ~a) | (~b & a) | (c & a); + case 0xbe: return (c) | (b & ~a) | (~b & a); + case 0xbf: return (~a) | (~b) | (c); + case 0xc0: return (b & a); + case 0xc1: return (~c & ~b & ~a) | (b & a); + case 0xc2: return (c & ~b & ~a) | (b & a); + case 0xc3: return (~b & ~a) | (b & a); + case 0xc4: return (~c & b) | (b & a); + case 0xc5: return (~c & ~a) | (b & a); + case 0xc6: return (c & ~b & ~a) | (~c & b) | (b & a); + case 0xc7: return (~b & ~a) | (~c & ~a) | (b & a); + case 0xc8: return (c & b) | (b & a); + case 0xc9: return (~c & ~b & ~a) | (c & b) | (b & a); + case 0xca: return (c & ~a) | (b & a); + case 0xcb: return (~b & ~a) | (c & ~a) | (b & a); + case 0xcc: return (b); + case 0xcd: return (~c & ~a) | (b); + case 0xce: return (c & ~a) | (b); + case 0xcf: return (~a) | (b); + case 0xd0: return (~c & a) | (b & a); + case 0xd1: return (~c & ~b) | (b & a); + case 0xd2: return (c & ~b & ~a) | (~c & a) | (b & a); + case 0xd3: return (~b & ~a) | (~c & a) | (b & a); + case 0xd4: return (~c & b) | (~c & a) | (b & a); + case 0xd5: return (~c) | (b & a); + case 0xd6: return (c & ~b & ~a) | (~c & b) | (~c & a) | (b & a); + case 0xd7: return (~b & ~a) | (~c) | (b & a); + case 0xd8: return (c & b) | (~c & a); + case 0xd9: return (~c & ~b) | (c & b) | (b & a); + case 0xda: return (c & ~a) | (~c & a) | (b & a); + case 0xdb: return (~b & ~a) | (c & ~a) | (~c & a) | (b & a); + case 0xdc: return (b) | (~c & a); + case 0xdd: return (~c) | (b); + case 0xde: return (c & ~a) | (b) | (~c & a); + case 0xdf: return (~a) | (~c) | (b); + case 0xe0: return (c & a) | (b & a); + case 0xe1: return (~c & ~b & ~a) | (c & a) | (b & a); + case 0xe2: return (c & ~b) | (b & a); + case 0xe3: return (~b & ~a) | (c & a) | (b & a); + case 0xe4: return (~c & b) | (c & a); + case 0xe5: return (~c & ~a) | (c & a) | (b & a); + case 0xe6: return (c & ~b) | (~c & b) | (b & a); + case 0xe7: return (~b & ~a) | (~c & ~a) | (c & a) | (b & a); + case 0xe8: return (c & b) | (c & a) | (b & a); + case 0xe9: return (~c & ~b & ~a) | (c & b) | (c & a) | (b & a); + case 0xea: return (c) | (b & a); + case 0xeb: return (~b & ~a) | (c) | (b & a); + case 0xec: return (b) | (c & a); + case 0xed: return (~c & ~a) | (b) | (c & a); + case 0xee: return (c) | (b); + case 0xef: return (~a) | (c) | (b); + case 0xf0: return (a); + case 0xf1: return (~c & ~b) | (a); + case 0xf2: return (c & ~b) | (a); + case 0xf3: return (~b) | (a); + case 0xf4: return (~c & b) | (a); + case 0xf5: return (~c) | (a); + case 0xf6: return (c & ~b) | (~c & b) | (a); + case 0xf7: return (~b) | (~c) | (a); + case 0xf8: return (c & b) | (a); + case 0xf9: return (~c & ~b) | (c & b) | (a); + case 0xfa: return (c) | (a); + case 0xfb: return (~b) | (c) | (a); + case 0xfc: return (b) | (a); + case 0xfd: return (~c) | (b) | (a); + case 0xfe: return (c) | (b) | (a); + case 0xff: return 0xffff; + } + } + + function blitter_dofast() { + var i,j; + var bltadatptr = 0, bltbdatptr = 0, bltcdatptr = 0, bltddatptr = 0; + var mt = bltcon0 & 0xFF; + + blit_masktable[0] = blt_info.bltafwm; + blit_masktable[blt_info.hblitsize - 1] &= blt_info.bltalwm; + + if (bltcon0 & 0x800) { + bltadatptr = bltapt; + bltapt += (blt_info.hblitsize * 2 + blt_info.bltamod) * blt_info.vblitsize; + } + if (bltcon0 & 0x400) { + bltbdatptr = bltbpt; + bltbpt += (blt_info.hblitsize * 2 + blt_info.bltbmod) * blt_info.vblitsize; + } + if (bltcon0 & 0x200) { + bltcdatptr = bltcpt; + bltcpt += (blt_info.hblitsize * 2 + blt_info.bltcmod) * blt_info.vblitsize; + } + if (bltcon0 & 0x100) { + bltddatptr = bltdpt; + bltdpt += (blt_info.hblitsize * 2 + blt_info.bltdmod) * blt_info.vblitsize; + } + + if (blitfunc_dofast[mt] !== 0 && !blitfill) + blitfunc_dofast[mt](bltadatptr, bltbdatptr, bltcdatptr, bltddatptr, blt_info); + else { + var blitbhold = blt_info.bltbhold; + var preva = 0, prevb = 0; + var dstp = 0; + var dodst = 0; + + for (j = 0; j < blt_info.vblitsize; j++) { + blitfc = !!(bltcon1 & 0x4); + for (i = 0; i < blt_info.hblitsize; i++) { + var bltadat, blitahold; + var bltbdat; + if (bltadatptr) { + blt_info.bltadat = bltadat = AMIGA.mem.load16_chip(bltadatptr); + bltadatptr += 2; + } else + bltadat = blt_info.bltadat; + bltadat &= blit_masktable[i]; + blitahold = (((preva << 16) | bltadat) >>> 0) >>> blt_info.blitashift; + preva = bltadat; + + if (bltbdatptr) { + blt_info.bltbdat = bltbdat = AMIGA.mem.load16_chip(bltbdatptr); + bltbdatptr += 2; + blitbhold = (((prevb << 16) | bltbdat) >>> 0) >>> blt_info.blitbshift; + prevb = bltbdat; + } + + if (bltcdatptr) { + blt_info.bltcdat = AMIGA.mem.load16_chip(bltcdatptr); + bltcdatptr += 2; + } + if (dodst) + AMIGA.mem.store16_chip(dstp, blt_info.bltddat); + blt_info.bltddat = blit_func (blitahold, blitbhold, blt_info.bltcdat, mt) & 0xffff; + if (blitfill) { + var d = blt_info.bltddat; + var ifemode = blitife ? 2 : 0; + var fc1 = blit_filltable[d & 255][ifemode + blitfc][1]; + blt_info.bltddat = (blit_filltable[d & 255][ifemode + blitfc][0] + (blit_filltable[d >> 8][ifemode + fc1][0] << 8)); + blitfc = blit_filltable[d >> 8][ifemode + fc1][1]; + } + if (blt_info.bltddat) + blt_info.blitzero = 0; + if (bltddatptr) { + dodst = 1; + dstp = bltddatptr; + bltddatptr += 2; + } + } + if (bltadatptr) bltadatptr += blt_info.bltamod; + if (bltbdatptr) bltbdatptr += blt_info.bltbmod; + if (bltcdatptr) bltcdatptr += blt_info.bltcmod; + if (bltddatptr) bltddatptr += blt_info.bltdmod; + } + if (dodst) + AMIGA.mem.store16_chip(dstp, blt_info.bltddat); + blt_info.bltbhold = blitbhold; + } + blit_masktable[0] = 0xffff; + blit_masktable[blt_info.hblitsize - 1] = 0xffff; + + bltstate = BLT_done; + } + + function blitter_dofast_desc() { + var i,j; + var bltadatptr = 0, bltbdatptr = 0, bltcdatptr = 0, bltddatptr = 0; + var mt = bltcon0 & 0xFF; + + blit_masktable[0] = blt_info.bltafwm; + blit_masktable[blt_info.hblitsize - 1] &= blt_info.bltalwm; + + if (bltcon0 & 0x800) { + bltadatptr = bltapt; + bltapt -= (blt_info.hblitsize * 2 + blt_info.bltamod) * blt_info.vblitsize; + } + if (bltcon0 & 0x400) { + bltbdatptr = bltbpt; + bltbpt -= (blt_info.hblitsize * 2 + blt_info.bltbmod) * blt_info.vblitsize; + } + if (bltcon0 & 0x200) { + bltcdatptr = bltcpt; + bltcpt -= (blt_info.hblitsize * 2 + blt_info.bltcmod) * blt_info.vblitsize; + } + if (bltcon0 & 0x100) { + bltddatptr = bltdpt; + bltdpt -= (blt_info.hblitsize * 2 + blt_info.bltdmod) * blt_info.vblitsize; + } + + if (blitfunc_dofast_desc[mt] !== 0 && !blitfill) + blitfunc_dofast_desc[mt](bltadatptr, bltbdatptr, bltcdatptr, bltddatptr, blt_info); + else { + var blitbhold = blt_info.bltbhold; + var preva = 0, prevb = 0; + var dstp = 0; + var dodst = 0; + + for (j = 0; j < blt_info.vblitsize; j++) { + blitfc = !!(bltcon1 & 0x4); + for (i = 0; i < blt_info.hblitsize; i++) { + var bltadat, blitahold; + var bltbdat; + if (bltadatptr) { + bltadat = blt_info.bltadat = AMIGA.mem.load16_chip(bltadatptr); + bltadatptr -= 2; + } else + bltadat = blt_info.bltadat; + bltadat &= blit_masktable[i]; + blitahold = (((bltadat << 16) | preva) >>> 0) >> blt_info.blitdownashift; + preva = bltadat; + + if (bltbdatptr) { + blt_info.bltbdat = bltbdat = AMIGA.mem.load16_chip(bltbdatptr); + bltbdatptr -= 2; + blitbhold = (((bltbdat << 16) | prevb) >>> 0) >> blt_info.blitdownbshift; + prevb = bltbdat; + } + + if (bltcdatptr) { + blt_info.bltcdat = blt_info.bltbdat = AMIGA.mem.load16_chip(bltcdatptr); + bltcdatptr -= 2; + } + if (dodst) + AMIGA.mem.store16_chip(dstp, blt_info.bltddat); + blt_info.bltddat = blit_func (blitahold, blitbhold, blt_info.bltcdat, mt) & 0xffff; + if (blitfill) { + var d = blt_info.bltddat; + var ifemode = blitife ? 2 : 0; + var fc1 = blit_filltable[d & 255][ifemode + blitfc][1]; + blt_info.bltddat = (blit_filltable[d & 255][ifemode + blitfc][0] + (blit_filltable[d >> 8][ifemode + fc1][0] << 8)); + blitfc = blit_filltable[d >> 8][ifemode + fc1][1]; + } + if (blt_info.bltddat) + blt_info.blitzero = 0; + if (bltddatptr) { + dstp = bltddatptr; + dodst = 1; + bltddatptr -= 2; + } + } + if (bltadatptr) bltadatptr -= blt_info.bltamod; + if (bltbdatptr) bltbdatptr -= blt_info.bltbmod; + if (bltcdatptr) bltcdatptr -= blt_info.bltcmod; + if (bltddatptr) bltddatptr -= blt_info.bltdmod; + } + if (dodst) + AMIGA.mem.store16_chip(dstp, blt_info.bltddat); + blt_info.bltbhold = blitbhold; + } + blit_masktable[0] = 0xffff; + blit_masktable[blt_info.hblitsize - 1] = 0xffff; + + bltstate = BLT_done; + } + + function blitter_read() { + if (bltcon0 & 0x200) { + if (AMIGA.dmaen(DMAF_BLTEN)) + blt_info.bltcdat = AMIGA.mem.load16_chip(bltcpt); + } + bltstate = BLT_work; + } + + function blitter_write() { + if (blt_info.bltddat) + blt_info.blitzero = 0; + + if (bltcon0 & 0x200) { + if (AMIGA.dmaen(DMAF_BLTEN)) + AMIGA.mem.store16_chip(bltdpt, blt_info.bltddat); + } + bltstate = BLT_next; + } + + function blitter_line() { + var blitahold = (blinea & blt_info.bltafwm) >>> blinea_shift; + var blitchold = blt_info.bltcdat; + + blt_info.bltbhold = (blineb & 1) ? 0xffff : 0; + blitlinepixel = !blitsing || (blitsing && !blitonedot); + blt_info.bltddat = blit_func (blitahold, blt_info.bltbhold, blitchold, bltcon0 & 0xFF); + blitonedot++; + } + + /*function blitter_line_incx() { + if (++blinea_shift == 16) { + blinea_shift = 0; + bltcpt += 2; + } + } + function blitter_line_decx() { + if (blinea_shift-- == 0) { + blinea_shift = 15; + bltcpt -= 2; + } + } + function blitter_line_decy() { + bltcpt -= blt_info.bltcmod; + blitonedot = 0; + } + function blitter_line_incy() { + bltcpt += blt_info.bltcmod; + blitonedot = 0; + } + function blitter_line_proc() { + if (bltcon0 & 0x800) { + if (blitsign) + bltapt_line += blt_info.bltbmod; + else + bltapt_line += blt_info.bltamod; + } + if (!blitsign) { + if (bltcon1 & 0x10) { + if (bltcon1 & 0x8) { + blitter_line_decy(); + } else { + blitter_line_incy(); + } + } else { + if (bltcon1 & 0x8) { + blitter_line_decx(); + } else { + blitter_line_incx(); + } + } + } + if (bltcon1 & 0x10) { + if (bltcon1 & 0x4) { + blitter_line_decx(); + } else { + blitter_line_incx(); + } + } else { + if (bltcon1 & 0x4) { + blitter_line_decy(); + } else { + blitter_line_incy(); + } + } + blitsign = 0 > bltapt_line; + bltstate = BLT_write; + }*/ + + function blitter_line_proc_fast() { + if (bltcon0 & 0x800) { + if (blitsign) + bltapt_line += blt_info.bltbmod; + else + bltapt_line += blt_info.bltamod; + } + if (!blitsign) { + if (bltcon1 & 0x10) { + if (bltcon1 & 0x8) { + bltcpt -= blt_info.bltcmod; + blitonedot = 0; + } else { + bltcpt += blt_info.bltcmod; + blitonedot = 0; + } + } else { + if (bltcon1 & 0x8) { + if (blinea_shift-- == 0) { + blinea_shift = 15; + bltcpt -= 2; + } + } else { + if (++blinea_shift == 16) { + blinea_shift = 0; + bltcpt += 2; + } + } + } + } + if (bltcon1 & 0x10) { + if (bltcon1 & 0x4) { + if (blinea_shift-- == 0) { + blinea_shift = 15; + bltcpt -= 2; + } + } else { + if (++blinea_shift == 16) { + blinea_shift = 0; + bltcpt += 2; + } + } + } else { + if (bltcon1 & 0x4) { + bltcpt -= blt_info.bltcmod; + blitonedot = 0; + } else { + bltcpt += blt_info.bltcmod; + blitonedot = 0; + } + } + blitsign = 0 > bltapt_line; + bltstate = BLT_write; + } + + function blitter_nxline() { + blineb = ((blineb << 1) | (blineb >> 15)) & 0xffff; + blt_info.vblitsize--; + bltstate = BLT_read; + } + + function actually_do_blit() { + if (blitline) { + bltapt_line = bltapt & 0xffff; if (bltapt_line & 0x8000) bltapt_line -= 0x10000; + do { + blitter_read(); + if (ddat1use) + bltdpt = bltcpt; + ddat1use = 1; + blitter_line(); + blitter_line_proc_fast(); + blitter_nxline(); + if (blitlinepixel) { + blitter_write(); + blitlinepixel = 0; + } + if (blt_info.vblitsize <= 0) + bltstate = BLT_done; + } while (bltstate != BLT_done); + bltdpt = bltcpt; + } else { + if (blitdesc) + blitter_dofast_desc(); + else + blitter_dofast(); + bltstate = BLT_done; + } + } + + function blitter_doit() { + actually_do_blit(); + blitter_done(AMIGA.events.hpos()); + } + + /*---------------------------------*/ + + this.handler = function(data) { + if (!AMIGA.dmaen(DMAF_BLTEN)) { + AMIGA.events.event2_newevent(EV2_BLITTER, 10, 0); + if (++blit_stuck < 20000 || !AMIGA.config.blitter.immediate) + return; + + BUG.info('blitter_handler() force-unstuck!'); + } + blit_stuck = 0; + if (blit_slowdown > 0 && !AMIGA.config.blitter.immediate) { + AMIGA.events.event2_newevent(EV2_BLITTER, blit_slowdown, 0); + blit_slowdown = -1; + return; + } + blitter_doit(); + } + + /*function blitter_force_finish() { + if (bltstate == BLT_done) + return; + if (bltstate != BLT_done) { + var odmacon = AMIGA.dmacon; + AMIGA.dmacon |= DMAF_DMAEN | DMAF_BLTEN; + BUG.info('forcing blitter finish'); + + if (blitter_cycle_exact) { + var rounds = 10000; + while (bltstate != BLT_done && rounds > 0) { + memset (cycle_line, 0, sizeof cycle_line); + decide_blitter (-1); + rounds--; + } + if (rounds == 0) BUG.info('blitter froze!?'); + blit_startcycles = 0; + } else { + actually_do_blit(); + } + blitter_done(AMIGA.events.hpos()); + AMIGA.dmacon = odmacon; + } + }*/ + + /*var changetable = new Uint8Array(32 * 32); + for (var i = 0; i < changetable.length; i++) changetable[i] = 0;*/ + var freezes = 10; + function blit_bltset(con) { + if (con & 2) { + blitdesc = bltcon1 & 2; + blt_info.blitbshift = bltcon1 >> 12; + blt_info.blitdownbshift = 16 - blt_info.blitbshift; + } + + if (con & 1) { + blt_info.blitashift = bltcon0 >> 12; + blt_info.blitdownashift = 16 - blt_info.blitashift; + } + + blit_ch = (bltcon0 & 0x0f00) >> 8; + blitline = (bltcon1 & 1) != 0; + blitfill = !!(bltcon1 & 0x18); + + if (bltstate != BLT_done && blitline) { + blitline = 0; + bltstate = BLT_done; + blit_interrupt = true; + BUG.info('blit_bltset() register modification during linedraw! (%d)', bltstate); + } + + if (blitline) { + if (blt_info.hblitsize != 2) + BUG.info('blit_bltset() weird hsize in linemode: %d vsize=%d', blt_info.hblitsize, blt_info.vblitsize); + blit_diag = blit_cycle_diagram_line; + } else { + if (con & 2) { + blitfc = !!(bltcon1 & 0x4); + blitife = !!(bltcon1 & 0x8); + if ((bltcon1 & 0x18) == 0x18) { + //BUG.info('blit_bltset() weird fill mode'); + blitife = 0; + } + } + //if (blitfill && !blitdesc) BUG.info('blit_bltset() fill without desc'); + + blit_diag = blitfill && blit_cycle_diagram_fill[blit_ch][0] ? blit_cycle_diagram_fill[blit_ch] : blit_cycle_diagram[blit_ch]; + } + if ((bltcon1 & 0x80) && (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS)) + BUG.info('blit_bltset() ECS BLTCON1 DOFF-bit set'); + + /*on the fly switching fillmode from extra cycle to non-extra: blitter freezes + non-extra cycle to extra cycle: does not freeze but cycle diagram goes weird, + extra free cycle changes to another D write.. + (Absolute Inebriation vector cube inside semi-filled vector object requires freezing blitter.)*/ + /*if (bltstate != BLT_done) { + var olddiag = blit_diag; + var isen = blit_diag >= &blit_cycle_diagram_fill[0][0] && blit_diag <= &blit_cycle_diagram_fill[15][0]; + var iseo = olddiag >= &blit_cycle_diagram_fill[0][0] && olddiag <= &blit_cycle_diagram_fill[15][0]; + if (iseo != isen) { + if (freezes > 0) { + BUG.info('BLITTER: on the fly %d (%d) -> %d (%d) switch!', original_ch, iseo, blit_ch, isen); + freezes--; + } + } + if (original_fill == isen) { + blit_frozen = 0; + } else if (iseo && !isen) { + blit_frozen = 1; + BUG.info('BLITTER: frozen! %d (%d) -> %d (%d)', original_ch, iseo, blit_ch, isen); + } else if (!iseo && isen) { + BUG.info('BLITTER: on the fly %d (%d) -> %d (%d) switch', original_ch, iseo, blit_ch, isen); + } + }*/ + + // on the fly switching from CH=1 to CH=D -> blitter stops writing (Rampage/TEK) + // currently just switch to no-channels mode, better than crashing the demo.. + if (bltstate != BLT_done) { + /*var o = original_ch + (original_fill ? 16 : 0); + var n = blit_ch + (blitfill ? 16 : 0); + if (o != n) { + if (changetable[o * 32 + n] < 10) { + changetable[o * 32 + n]++; + BUG.info('blit_bltset() channel mode changed while active (%02x->%02x)', o, n); + } + }*/ + if (blit_ch == 13 && original_ch == 1) + blit_faulty = 1; + } + + if (blit_faulty) { + //BUG.info('blit_bltset() blit_faulty!'); + blit_ch = 0; + blit_diag = blit_cycle_diagram[blit_ch]; + } + + blit_dmacount = blit_dmacount2 = 0; + blit_nod = 1; + for (var i = 0; i < blit_diag[0]; i++) { + var v = blit_diag[1 + blit_diag[0] + i]; + if (v <= 4) + blit_dmacount++; + if (v > 0 && v < 4) + blit_dmacount2++; + if (v == 4) + blit_nod = 0; + } + } + + /*function blit_modset() { + blit_add = blitdesc ? -2 : 2; + var mult = blitdesc ? -1 : 1; + blit_modadda = mult * blt_info.bltamod; + blit_modaddb = mult * blt_info.bltbmod; + blit_modaddc = mult * blt_info.bltcmod; + blit_modaddd = mult * blt_info.bltdmod; + }*/ + + function reset_blit(bltcon) { + if (bltcon & 1) + blinea_shift = bltcon0 >> 12; + if (bltcon & 2) + blitsign = (bltcon1 & 0x40) != 0; + if (bltstate == BLT_done) + return; + if (bltcon) + blit_bltset(bltcon); + //blit_modset(); + } + + var warned1 = 10; + function waitingblits() { + var waited = false; + while (bltstate != BLT_done && AMIGA.dmaen(DMAF_BLTEN)) { + waited = true; + AMIGA.events.cycle(8 * CYCLE_UNIT); + } + if (warned1 && waited) { + warned1--; + BUG.info('waiting_blits detected'); + } + if (bltstate == BLT_done) + return true; + return false; + } + + function do_blitter2(hpos, copper) { + var cycles; + + //#ifdef BLITTER_DEBUG_NOWAIT + if (bltstate != BLT_done) { + if (blit_final) BUG.info('do_blitter2() blitter was already active!'); + } + //#endif + var cleanstart = 0; + if (bltstate == BLT_done) { + if (blit_faulty > 0) + blit_faulty = 0; + cleanstart = 1; + } + + bltstate = BLT_done; + + blitter_cycle_exact = AMIGA.config.blitter.exact; + blt_info.blitzero = 1; + preva = 0; + prevb = 0; + blt_info.got_cycle = 0; + //blit_frozen = 0; + + blit_firstline_cycles = blit_first_cycle = AMIGA.events.currcycle; + //blit_misscyclecounter = 0; + blit_last_cycle = 0; + //blit_maxcyclecounter = 0; + //last_blitter_hpos = hpos + 1; + blit_cyclecounter = 0; + //blit_totalcyclecounter = 0; + + blit_bltset(1 | 2); + //blit_modset(); + ddat1use = ddat2use = 0; + blit_interrupt = false; + + if (blitline) { + blinea = blt_info.bltadat; + blineb = ((blt_info.bltbdat >>> blt_info.blitbshift) | (blt_info.bltbdat << (16 - blt_info.blitbshift))) & 0xffff; + blitonedot = 0; + blitlinepixel = 0; + blitsing = (bltcon1 & 0x2) != 0; + cycles = blt_info.vblitsize; + } else { + blit_firstline_cycles = blit_first_cycle + (blit_diag[0] * blt_info.hblitsize + AMIGA.cpu.cycles) * CYCLE_UNIT; + cycles = blt_info.vblitsize * blt_info.hblitsize; + //console.log('blit_firstline_cycles', blit_firstline_cycles, cycles); + } + + if (cleanstart) { + original_ch = blit_ch; + original_fill = blitfill; + original_line = blitline; + } + + /*if (0) { + var ch = 0; + if (blit_ch & 1) + ch++; + if (blit_ch & 2) + ch++; + if (blit_ch & 4) + ch++; + if (blit_ch & 8) + ch++; + BUG.info('do_blitter2() %dx%d ch=%d %d*%d=%d d=%d f=%d n=%d l=%d dma=%04x %s', + blt_info.hblitsize, blt_info.vblitsize, ch, blit_diag[0], cycles, blit_diag[0] * cycles, + blitdesc ? 1 : 0, blitfill ? 1 : 0, AMIGA.dmaen(DMAF_BLTPRI) ? 1 : 0, blitline ? 1 : 0, + AMIGA.dmacon, AMIGA.dmaen(DMAF_BLTEN) ? 'on' : 'off!'); + blitter_dump(); + }*/ + + bltstate = BLT_init; + blit_slowdown = 0; + + clr_special (SPCFLAG_BLTNASTY); + if (AMIGA.dmaen(DMAF_BLTPRI)) + set_special (SPCFLAG_BLTNASTY); + + if (AMIGA.dmaen(DMAF_BLTEN)) + bltstate = BLT_work; + + /*blit_maxcyclecounter = 0x7fffffff; + if (blitter_cycle_exact) { + blitter_hcounter1 = blitter_hcounter2 = 0; + blitter_vcounter1 = blitter_vcounter2 = 0; + if (blit_nod) + blitter_vcounter2 = blt_info.vblitsize; + blit_cyclecounter = -BLITTER_STARTUP_CYCLES; + blit_waitcyclecounter = copper; + blit_startcycles = 0; + blit_maxcyclecounter = blt_info.hblitsize * blt_info.vblitsize + 2; + return; + }*/ + + if (blt_info.vblitsize == 0 || (blitline && blt_info.hblitsize != 2)) { + blitter_done(hpos); + return; + } + + blt_info.got_cycle = 1; + //blit_waitcyclecounter = 0; + + if (AMIGA.config.blitter.immediate) { + blitter_doit(); + return; + } + + blit_cyclecounter = cycles * (blit_dmacount2 + (blit_nod ? 0 : 1)); + AMIGA.events.event2_newevent(EV2_BLITTER, blit_cyclecounter, 0); + + if (AMIGA.dmaen(DMAF_BLTEN) && (AMIGA.config.cpu.model >= 68020 || !AMIGA.config.cpu.exact)) { + if (AMIGA.config.blitter.waiting) { + // wait immediately if all cycles in use and blitter nastry + if (blit_dmacount == blit_diag[0] && (AMIGA.spcflags & SPCFLAG_BLTNASTY)) + waitingblits(); + } + } + } + + function do_blitter(hpos, copper) { + if (bltstate == BLT_done || !AMIGA.config.blitter.exact) { + do_blitter2(hpos, copper); + return; + } + if (!AMIGA.dmaen(DMAF_BLTEN) || !blt_info.got_cycle) + return; + + //blit_startcycles = BLITTER_STARTUP_CYCLES; + //blit_waitcyclecounter = copper; + } + + var warned2 = 10; + function maybe_blit(hpos, hack) { + if (bltstate == BLT_done) + return; + + if (AMIGA.dmaen(DMAF_BLTEN) && (AMIGA.config.cpu.model >= 68020 || !AMIGA.config.cpu.exact)) { + var doit = false; + if (AMIGA.config.blitter.waiting == 3) { // always + doit = true; + } else if (AMIGA.config.blitter.waiting == 2) { // noidle + if (blit_dmacount == blit_diag[0] && (AMIGA.spcflags & SPCFLAG_BLTNASTY)) + doit = true; + } else if (AMIGA.config.blitter.waiting == 1) { // automatic + if (blit_dmacount == blit_diag[0] && (AMIGA.spcflags & SPCFLAG_BLTNASTY)) + doit = true; + else if (AMIGA.config.cpu.speed < 0) + doit = true; + } + if (doit) { + if (waitingblits ()) + return; + } + } + + if (warned2 && AMIGA.dmaen(DMAF_BLTEN) && blt_info.got_cycle) { + warned2--; + BUG.info('maybe_blit() program does not wait for blitter tc=%d', blit_cyclecounter); + } + + /*if (blitter_cycle_exact) { + decide_blitter (hpos); + return; + }*/ + if (hack == 1 && AMIGA.events.currcycle < blit_firstline_cycles) + return; + + AMIGA.blitter.handler(0); + } + + this.blitnasty = function() { + var cycles, ccnt; + if (bltstate == BLT_done) + return 0; + if (!AMIGA.dmaen(DMAF_BLTEN)) + return 0; + /*if (blitter_cycle_exact) { + blitter_force_finish(); + return -1; + }*/ + if (blit_last_cycle >= blit_diag[0] && blit_dmacount == blit_diag[0]) + return 0; + cycles = Math.floor((AMIGA.events.currcycle - blit_first_cycle) / CYCLE_UNIT); + ccnt = 0; + while (blit_last_cycle < cycles) { + var c = channel_state(blit_last_cycle++); + if (!c) + ccnt++; + } + return ccnt; + } + + /*---------------------------------*/ + + var oddfstrt = 0, oddfstop = 0, ototal = 0, ofree = 0, slow = 0; + this.slowdown = function() { + var data = AMIGA.playfield.get_data(); + var ddfstrt = data[0]; + var ddfstop = data[1]; + var totalcycles = data[2]; + var freecycles = data[3]; + + if (!totalcycles || ddfstrt < 0 || ddfstop < 0) + return; + if (ddfstrt != oddfstrt || ddfstop != oddfstop || totalcycles != ototal || ofree != freecycles) { + var linecycles = Math.floor(((ddfstop - ddfstrt + totalcycles - 1) / totalcycles) * totalcycles); + var freelinecycles = Math.floor(((ddfstop - ddfstrt + totalcycles - 1) / totalcycles) * freecycles); + var dmacycles = Math.floor((linecycles * blit_dmacount) / blit_diag[0]); + oddfstrt = ddfstrt; + oddfstop = ddfstop; + ototal = totalcycles; + ofree = freecycles; + slow = 0; + if (dmacycles > freelinecycles) + slow = dmacycles - freelinecycles; + } + if (blit_slowdown < 0 || blitline) + return; + + blit_slowdown += slow; + //blit_misscyclecounter += slow; + //console.log('Blitter.slowdown()', data, blit_slowdown); + } + + /*---------------------------------*/ + + this.BLTADAT = function(hpos, v) { maybe_blit(hpos, 0); blt_info.bltadat = v; } + this.BLTBDAT = function(hpos, v) { + maybe_blit(hpos, 0); + if (bltcon1 & 2) + blt_info.bltbhold = (v << (bltcon1 >> 12)) & 0xffff; + else + blt_info.bltbhold = (v >> (bltcon1 >> 12)) & 0xffff; + + blt_info.bltbdat = v; + } + this.BLTCDAT = function(hpos, v) { maybe_blit(hpos, 0); blt_info.bltcdat = v; reset_blit(0); } + + this.BLTAMOD = function(hpos, v) { maybe_blit(hpos, 1); blt_info.bltamod = castWord(v & 0xfffe); reset_blit(0); } + this.BLTBMOD = function(hpos, v) { maybe_blit(hpos, 1); blt_info.bltbmod = castWord(v & 0xfffe); reset_blit(0); } + this.BLTCMOD = function(hpos, v) { maybe_blit(hpos, 1); blt_info.bltcmod = castWord(v & 0xfffe); reset_blit(0); } + this.BLTDMOD = function(hpos, v) { maybe_blit(hpos, 1); blt_info.bltdmod = castWord(v & 0xfffe); reset_blit(0); } + + this.BLTCON0 = function(hpos, v) { maybe_blit(hpos, 2); bltcon0 = v; reset_blit(1); } + this.BLTCON0L = function( hpos, v) { + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_OCS) return; + maybe_blit(hpos, 2); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); + reset_blit(1); + } + this.BLTCON1 = function(hpos, v) { maybe_blit(hpos, 2); bltcon1 = v; reset_blit(2); } + + this.BLTAFWM = function(hpos, v) { maybe_blit(hpos, 2); blt_info.bltafwm = v; reset_blit(0); } + this.BLTALWM = function(hpos, v) { maybe_blit(hpos, 2); blt_info.bltalwm = v; reset_blit(0); } + + this.BLTAPTH = function(hpos, v) { maybe_blit(hpos, 0); bltapt = ((bltapt & 0xffff) | (v << 16)) >>> 0; } + this.BLTAPTL = function(hpos, v) { maybe_blit(hpos, 0); bltapt = ((bltapt & ~0xffff) | (v & 0xfffe)) >>> 0; } + this.BLTBPTH = function(hpos, v) { maybe_blit(hpos, 0); bltbpt = ((bltbpt & 0xffff) | (v << 16)) >>> 0; } + this.BLTBPTL = function(hpos, v) { maybe_blit(hpos, 0); bltbpt = ((bltbpt & ~0xffff) | (v & 0xfffe)) >>> 0; } + this.BLTCPTH = function(hpos, v) { maybe_blit(hpos, 0); bltcpt = ((bltcpt & 0xffff) | (v << 16)) >>> 0; } + this.BLTCPTL = function(hpos, v) { maybe_blit(hpos, 0); bltcpt = ((bltcpt & ~0xffff) | (v & 0xfffe)) >>> 0; } + this.BLTDPTH = function(hpos, v) { maybe_blit(hpos, 0); bltdpt = ((bltdpt & 0xffff) | (v << 16)) >>> 0; } + this.BLTDPTL = function(hpos, v) { maybe_blit(hpos, 0); bltdpt = ((bltdpt & ~0xffff) | (v & 0xfffe)) >>> 0; } + + this.BLTSIZE = function(hpos, v) { + maybe_blit(hpos, 0); + + blt_info.vblitsize = v >> 6; + blt_info.hblitsize = v & 0x3F; + if (!blt_info.vblitsize) + blt_info.vblitsize = 1024; + if (!blt_info.hblitsize) + blt_info.hblitsize = 64; + do_blitter(hpos, AMIGA.copper.access); + } + + this.BLTSIZV = function(hpos, v) { + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_OCS) return; + maybe_blit(hpos, 0); + blt_info.vblitsize = v & 0x7FFF; + } + + this.BLTSIZH = function(hpos, v) { + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_OCS) return; + maybe_blit(hpos, 0); + blt_info.hblitsize = v & 0x7FF; + if (!blt_info.vblitsize) + blt_info.vblitsize = 0x8000; + if (!blt_info.hblitsize) + blt_info.hblitsize = 0x0800; + do_blitter(hpos, AMIGA.copper.access); + } + + /*---------------------------------*/ + + this.getState = function() { + return bltstate; + } + this.setState = function(s) { + bltstate = s; + } + this.getIntZero = function() { + return [blit_interrupt, blt_info.blitzero]; + } +} diff --git a/sae/cia.js b/sae/cia.js new file mode 100644 index 0000000..1b742e4 --- /dev/null +++ b/sae/cia.js @@ -0,0 +1,738 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function CIA_8520(type) { + const STARTCYCLESHI = 3; + const STARTCYCLESCRA = 2; + + this.type = type; + + this.icr = 0; + this.icrReg = 0; + this.iMask = 0; + this.iFlag = type == CIA_A ? INTF_PORTS : INTF_EXTER; /* 0x0008 : 0x2000 */ + + this.cra = 0; + this.crb = 0; + this.dra = 0; + this.drb = 0; + this.pra = 0; + this.prb = 0; + this.sdr = 0; + this.sdrCnt = 0; + + this.ta = 0; + this.tb = 0; + this.taPassed = 0; + this.tbPassed = 0; + this.la = 0; + this.lb = 0; + this.starta = 0; + this.startb = 0; + + this.todOn = false; + this.tod = 0; + this.tol = 0; + this.tlatch = 0; + this.alarm = 0; + + this.reset = function() { + this.icr = this.iMask = 0; + this.cra = this.crb = 0x4; + this.dra = this.drb = 0; + this.pra = this.type == CIA_A ? 0 : 0x8c; + this.sdr = 0; + this.sdrCnt = 0; + this.la = this.lb = this.ta = this.tb = 0xffff; + this.taPassed = this.tbPassed = 0; + this.tod = 0; + this.todOn = false; + this.tlatch = 0; + this.alarm = 0; + } + + this.dump = function() { + BUG.info('CIA_8520.dump() %s | pra $%02x prb $%02x | dra $%02x drb $%02x | ta %5d (la %5d) tb %5d (lb %5d) | tod %5d (tol %5d) alarm %5d latch %d | sdr $%02x | icr $%02x iMask $%02x | cra $%02x crb $%02x', + this.type == CIA_A ? 'A' : 'B', this.pra, this.prb, this.dra, this.drb, this.ta, this.la, this.tb, this.lb, this.tod, this.tol, this.alarm, this.latch ? 1 : 0, this.sdr, this.icr, this.iMask, this.cra, this.crb); + } + + this.setClr = function (val) { + if (val & 0x80) this.iMask |= val & 0x7f; + else this.iMask &= ~val; + } + + this.setICR = function (icr, sdr) { + if (sdr !== null) + this.sdr = sdr; + + this.icr |= icr; + this.reThink(); + } + + this.ICR = function() { + if (this.iMask & this.icr) { + this.icr |= 0x80; + AMIGA.INTREQ_0(INTF_SETCLR | this.iFlag); + } + this.icrReg |= this.icr; + } + + this.reThink = function() { + if (this.icr) { + if (AMIGA.config.cpu.exact) + AMIGA.events.event2_newevent_xx(-1, CIA_RETHINK_DELAY, this.type, function(v) { AMIGA.cia.ICR(v); }); + else + this.ICR(); + } + } + + this.updateCheck = function (ciaclocks) { + var ovfla = false; + var ovflb = false; + var sp = false; + var needReThink = false; + + if ((this.cra & 0x21) == 0x01) { + //BUG.info('CIA.updateCheck() ciaclocks %d, starta %d, ta %d', ciaclocks, this.starta, this.ta); + var check = true; + var cc = ciaclocks; + if (this.starta > 0) { + if (cc > this.starta) { + cc -= this.starta; + this.starta = 0; + } else { + this.starta -= cc; + check = false; + } + } + if (check) { + //assert ((this.ta + 1) >= cc); + if ((this.ta + 1) == cc) { + if ((this.cra & 0x48) == 0x40 && this.sdrCnt > 0 && --this.sdrCnt == 0) sp = true; + + ovfla = true; + if ((this.crb & 0x61) == 0x41 || (this.crb & 0x61) == 0x61) { + if (this.tb-- == 0) ovflb = true; + } + } + this.ta -= cc; + } + } + if ((this.crb & 0x61) == 0x01) { + //BUG.info('CIA.updateCheck() ciaclocks %d, startb %d, tb %d', ciaclocks, this.startb, this.tb); + var check = true; + var cc = ciaclocks; + if (this.startb > 0) { + if (cc > this.startb) { + cc -= this.startb; + this.startb = 0; + } else { + this.startb -= cc; + check = false; + } + } + if (check) { + //assert ((this.tb + 1) >= cc); + if ((this.tb + 1) == cc) ovflb = true; + this.tb -= cc; + } + } + + if (ovfla) { + this.icr |= 1; + needReThink = true; + this.ta = this.la; + if (this.cra & 0x8) { + this.cra &= ~1; + } + } + if (ovflb) { + this.icr |= 2; + needReThink = true; + this.tb = this.lb; + if (this.crb & 0x8) { + this.crb &= ~1; + } + } + if (sp) { + this.icr |= 8; + needReThink = true; + } + return needReThink; + } + + this.calcTimers = function (div10diff) { + var time = [-1, -1]; + + if ((this.cra & 0x21) == 0x01) time[0] = div10diff + DIV10 * (this.ta + this.starta); + if ((this.crb & 0x61) == 0x01) time[1] = div10diff + DIV10 * (this.tb + this.startb); + + //if (time[0] != -1 || time[1] != -1) BUG.info('CIA.calcTimers() div10diff %d, timea %d, timeb %d', div10diff, time[0], time[1]); + return time; + } + + this.checkAlarm = function (inc) { + var alarm; + + if (this.tod == this.alarm) alarm = true; + else if (!inc) alarm = false; + else if (this.tod & 0x000fff) alarm = false; + else if (((this.tod - 1) & 0xfff000) == this.alarm) alarm = true; + else alarm = false; + + if (alarm) { + this.icr |= 4; + this.reThink(); + } + } + + this.calcPassedTime = function() { + var ccount = AMIGA.events.currcycle - AMIGA.events.eventtab[EV_CIA].oldcycles + AMIGA.cia.div10; + var ciaclocks = Math.floor(ccount / DIV10); + + this.taPassed = this.tbPassed = 0; + + if ((this.cra & 0x21) == 0x01) { + var cc = ciaclocks; + if (cc > this.starta) cc -= this.starta; + else cc = 0; + //assert ((this.ta + 1) >= cc); + this.taPassed = cc; + } + if ((this.crb & 0x61) == 0x01) { + var cc = ciaclocks; + if (cc > this.startb) cc -= this.startb; + else cc = 0; + //assert ((this.tb + 1) >= cc); + this.tbPassed = cc; + } + } + + this.read = function (addr) { + var reg = addr & 15; + var tmp; + + this.calcPassedTime(); + + switch (reg) { + case 0: { + if (this.type == CIA_A) { + tmp = AMIGA.disk.status() & 0x3c; + + if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Mouse) { + if (!AMIGA.input.mouse.button[0]) tmp |= 0x40; + if (this.dra & 0x40) tmp = (tmp & ~0x40) | (this.pra & 0x40); + } else if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Joy0) { + if (!AMIGA.input.joystick[0].button[0]) tmp |= 0x40; + if (this.dra & 0x40) tmp = (tmp & ~0x40) | (this.pra & 0x40); + } else tmp |= 0x40; + + if (AMIGA.config.ports[1].type == SAEV_Config_Ports_Type_Joy1) { + if (!AMIGA.input.joystick[1].button[0]) tmp |= 0x80; + if (this.dra & 0x80) tmp = (tmp & ~0x80) | (this.pra & 0x80); + } else tmp |= 0x80; + + tmp |= (this.pra | (this.dra ^ 3)) & 3; + } else { + //tmp = 0xf8; + tmp = AMIGA.serial.readStatus(this.dra); + } + return tmp; + } + case 1: { + if (this.type == CIA_A) { + tmp = 0; + } else { + tmp = this.prb; + } + if (this.crb & 2) { + var pb7 = 0; + if (this.crb & 4) pb7 = this.crb & 1; + tmp &= ~0x80; + tmp |= pb7 ? 0x80 : 0; + } + if (this.cra & 2) { + var pb6 = 0; + if (this.cra & 4) pb6 = this.cra & 1; + tmp &= ~0x40; + tmp |= pb6 ? 0x40 : 0; + } + return tmp; + } + case 2: + return this.dra; + case 3: + return this.drb; + case 4: + return ((this.ta - this.taPassed) & 0xff); + case 5: + return ((this.ta - this.taPassed) >> 8) & 0xff; + case 6: + return ((this.tb - this.tbPassed) & 0xff); + case 7: + return ((this.tb - this.tbPassed) >> 8) & 0xff; + case 8: { + if (this.tlatch) { + this.tlatch = 0; + return this.tol & 0xff; + } else return this.tod & 0xff; + } + case 9: { + if (this.tlatch) return (this.tol >> 8) & 0xff; + else return (this.tod >> 8) & 0xff; + } + case 10: { + if (!this.tlatch) { + if (!(this.crb & 0x80)) this.tlatch = 1; + this.tol = this.tod; + } + return (this.tol >>> 16) & 0xff; + } + case 12: + return this.sdr; + case 13: { + tmp = this.icrReg; + this.icr &= ~this.icrReg; + this.icrReg = 0; + this.reThink(); + return tmp; + } + case 14: + return this.cra; + case 15: + return this.crb; + } + } + + this.write = function (addr, val) { + var reg = addr & 15; + + switch (reg) { + case 0: { + if (this.type == CIA_A) { + this.pra = (this.pra & 0x3c) | (val & 0xc3); + AMIGA.cia.handlePowerLED(this.pra, this.dra); + } else { + this.pra = val; + AMIGA.serial.writeStatus(this.pra, this.dra); + } + break; + } + case 1: { + if (this.type == CIA_A) { + this.prb = val; + } else { + this.prb = val; + AMIGA.disk.select(val); + } + break; + } + case 2: { + if (this.type == CIA_A) { + this.dra = val; + AMIGA.cia.handlePowerLED(this.pra, this.dra); + } else { + this.dra = val; + AMIGA.serial.writeStatus(this.pra, this.dra); + } + break; + } + case 3: { + if (this.type == CIA_A) { + this.drb = val; + } else { + this.drb = val; + } + break; + } + case 4: { + AMIGA.cia.update(); + this.la = (this.la & 0xff00) | val; + AMIGA.cia.calcTimers(); + break; + } + case 5: { + AMIGA.cia.update(); + this.la = (this.la & 0x00ff) | (val << 8); + if ((this.cra & 1) == 0) this.ta = this.la; + if (this.cra & 8) { + this.ta = this.la; + this.cra |= 1; + this.starta = STARTCYCLESHI; + } + AMIGA.cia.calcTimers(); + break; + } + case 6: { + AMIGA.cia.update(); + this.lb = (this.lb & 0xff00) | val; + AMIGA.cia.calcTimers(); + break; + } + case 7: { + AMIGA.cia.update(); + this.lb = (this.lb & 0x00ff) | (val << 8); + if ((this.crb & 1) == 0) this.tb = this.lb; + if (this.crb & 8) { + this.tb = this.lb; + this.crb |= 1; + this.startb = STARTCYCLESHI; + } + AMIGA.cia.calcTimers(); + break; + } + case 8: { + if (this.crb & 0x80) { + this.alarm = (this.alarm & 0xffff00) | val; + } else { + this.tod = (this.tod & 0xffff00) | val; + this.todOn = true; + this.checkAlarm(0); + } + break; + } + case 9: { + if (this.crb & 0x80) { + this.alarm = (this.alarm & 0xff00ff) | (val << 8); + } else { + this.tod = (this.tod & 0xff00ff) | (val << 8); + } + break; + } + case 10: { + if (this.crb & 0x80) { + this.alarm = (this.alarm & 0x00ffff) | (val << 16); + } else { + this.tod = (this.tod & 0x00ffff) | (val << 16); + this.todOn = false; + } + break; + } + case 12: { + AMIGA.cia.update(); + this.sdr = val; + if (this.type == CIA_B && (this.cra & 0x40) == 0) this.sdrCnt = 0; + if ((this.cra & 0x41) == 0x41 && this.sdrCnt == 0) this.sdrCnt = 8 * 2; + AMIGA.cia.calcTimers(); + break; + } + case 13: { + this.setClr(val); + break; + } + case 14: { + AMIGA.cia.update(); + val &= 0x7f; + if ((val & 1) && !(this.cra & 1)) this.starta = STARTCYCLESCRA; + if (this.type == CIA_A && (val & 0x40) != (this.cra & 0x40)) { + AMIGA.input.keyboard.lostsynccnt = 0; + //BUG.info('KB_ACK %02x to %02x', val, this.cra); + } + this.cra = val; + if (this.cra & 0x10) { + this.cra &= ~0x10; + this.ta = this.la; + } + AMIGA.cia.calcTimers(); + break; + } + case 15: { + AMIGA.cia.update(); + if ((val & 1) && !(this.crb & 1)) this.startb = STARTCYCLESCRA; + this.crb = val; + if (this.crb & 0x10) { + this.crb &= ~0x10; + this.tb = this.lb; + } + AMIGA.cia.calcTimers(); + break; + } + } + } +} + +function CIA() { + this.A = new CIA_8520(CIA_A); + this.B = new CIA_8520(CIA_B); + this.div10 = 0; + + var led = true; + + var todHack = { + active: true, + enabled: false, + rate: 0.0, + tv: 0.0 + }; + + this.setup = function() { + if (todHack.active) todHack.rate = 1000.0 / AMIGA.events.hz; + } + + this.reset = function() { + this.A.reset(); + this.B.reset(); + this.div10 = 0; + this.calcTimers(); + + led = true; + AMIGA.disk.select_set(this.B.prb); + + if (todHack.active) { + todHack.enabled = AMIGA.events.maxvpos * AMIGA.events.hz * 10; + todHack.tv = 0.0; + } + } + + this.dump = function() { + this.A.dump(); + this.B.dump(); + } + + this.handlePowerLED = function (pra, dra) { + var v = pra | (~dra & 0xff); + var newled = (v & 2) ? 0 : 1; + + if (led != newled) { + led = newled; + AMIGA.audio.filter.led_filter_on = led; + AMIGA.config.hooks.power_led(led); + } + } + + this.setICR = function (type, icr, sdr) { + if (type == CIA_A) + this.A.setICR(icr, sdr); + else + this.B.setICR(icr, sdr); + } + + this.ICR = function(type) { + if (type == CIA_A) + this.A.ICR(); + else + this.B.ICR(); + } + + this.reThink = function() { + this.A.reThink(); + this.B.reThink(); + } + + this.update = function() { + var ccount = AMIGA.events.currcycle - AMIGA.events.eventtab[EV_CIA].oldcycles + this.div10; + var ciaclocks = Math.floor(ccount / DIV10); + + this.div10 = ccount % DIV10; + + //BUG.info('CIA.update() %d %d', ciaclocks, this.div10); + + var a = this.A.updateCheck(ciaclocks); + var b = this.B.updateCheck(ciaclocks); + if (a) this.A.reThink(); + if (b) this.B.reThink(); + } + + this.calcTimers = function() { + AMIGA.events.eventtab[EV_CIA].oldcycles = AMIGA.events.currcycle; + + var div10diff = DIV10 - this.div10; + var a = this.A.calcTimers(div10diff); + var b = this.B.calcTimers(div10diff); + + AMIGA.events.eventtab[EV_CIA].active = (a[0] != -1 || a[1] != -1 || b[0] != -1 || b[1] != -1); + if (AMIGA.events.eventtab[EV_CIA].active) { + var evtime = 0xffffffff; + + if (a[0] != -1) evtime = a[0]; + if (a[1] != -1 && a[1] < evtime) evtime = a[1]; + if (b[0] != -1 && b[0] < evtime) evtime = b[0]; + if (b[1] != -1 && b[1] < evtime) evtime = b[1]; + + AMIGA.events.eventtab[EV_CIA].evtime = evtime + AMIGA.events.currcycle; + //BUG.info('CIA.calcTimers() evtime %d', evtime); + } + AMIGA.events.schedule(); + } + + this.handler = function() { + this.update(); + this.calcTimers(); + } + + this.handleTodHack = function() { + if (todHack.enabled > 1) { + todHack.enabled--; + if (todHack.enabled == 1) { + todHack.tv = new Date().getTime(); + BUG.info('CIA.handleTodHack() enabled'); + } + return; + } + if (new Date().getTime() - todHack.tv >= todHack.rate) { + this.A.tod++; + this.A.tod &= 0xffffff; + this.A.checkAlarm(0); + todHack.tv += todHack.rate; + } + } + + this.hSyncPre = function() { + + } + + this.hSyncPost = function (doTod) { + if (this.B.todOn && doTod) { + this.B.tod++; + this.B.tod &= 0xffffff; + this.B.checkAlarm(1); + } + + if (todHack.active && this.A.todOn) this.handleTodHack(); + + AMIGA.input.keyboard.hsync(); + } + + this.vSyncPre = function() { + this.handler(); + AMIGA.input.keyboard.vsync(); + } + + this.vSyncPost = function (doTod) { + if (todHack.active && todHack.enabled == 1) return; + + if (this.A.todOn && doTod) { + this.A.tod++; + this.A.tod &= 0xffffff; + this.A.checkAlarm(1); + } + } + + this.waitPre = function() { + //if (AMIGA.config.cpu.cachesize) return; + +//#ifndef CUSTOM_SIMPLE + /*var div = (AMIGA.events.currcycle - AMIGA.events.eventtab[EV_CIA].oldcycles) % DIV10; + var div2 = Math.floor(DIV10 * ECLOCK_DATA_CYCLE / 10); + var cycles; + + if (div >= div2) + cycles = DIV10 - div + div2; + else if (div) + cycles = DIV10 + div2 - div; + else + cycles = div2 - div; + + //BUG.info('CIA.waitPre() %d %d', div, cycles); + + if (cycles) { + if (AMIGA.config.cpu.exact) + AMIGA.events.cycle(cycles); + else + AMIGA.events.cycle(cycles); + }*/ +//#endif + } + + this.waitPost = function () { + AMIGA.events.cycle(6 * CYCLE_UNIT / 2); + + /*if (AMIGA.config.cpu.cachesize) + AMIGA.events.cycle(8 * CYCLE_UNIT /2); + else*/ { + var c = 6 * CYCLE_UNIT / 2; + if (AMIGA.config.cpu.exact) + AMIGA.events.cycle(c); + else + AMIGA.events.cycle(c); + } + } + + this.load8 = function (addr) { + var v, r = (addr & 0xf00) >> 8; + + this.waitPre(); + switch ((addr >> 12) & 3) { + case 0: + v = (addr & 1) ? this.A.read(r) : this.B.read(r); + break; + case 1: + v = (addr & 1) ? 0xff : this.B.read(r); + break; + case 2: + v = (addr & 1) ? this.A.read(r) : 0xff; + break; + case 3: { + v = 0xff; + //if (AMIGA.config.cpu.model == 68000 && AMIGA.config.cpu.compatible) v = (addr & 1) ? regs.irc : regs.irc >> 8; + BUG.info('CIA.load8() unknown CIA address $%08x', addr); + break; + } + } + this.waitPost(); + return v; + } + + this.load16 = function (addr) { + var v, r = (addr & 0xf00) >> 8; + + this.waitPre(); + switch ((addr >> 12) & 3) { + case 0: + v = (this.B.read(r) << 8) | this.A.read(r); + break; + case 1: + v = (this.B.read(r) << 8) | 0xff; + break; + case 2: + v = (0xff << 8) | this.A.read(r); + break; + case 3: { + v = 0xffff; + //if (AMIGA.config.cpu.model == 68000 && AMIGA.config.cpu.compatible) v = regs.irc; + BUG.info('CIA.load16() unknown CIA address $%08x', addr); + break; + } + } + this.waitPost(); + return v; + } + + this.load32 = function (addr) { + return ((this.load16(addr) << 16) | this.load16(addr + 2)) >>> 0; + } + + this.store8 = function (addr, value) { + var r = (addr & 0xf00) >> 8; + + this.waitPre(); + if ((addr & 0x3000) != 0) { + if ((addr & 0x2000) == 0) this.B.write(r, value); + if ((addr & 0x1000) == 0) this.A.write(r, value); + } + this.waitPost(); + } + + this.store16 = function (addr, value) { + var r = (addr & 0xf00) >> 8; + + this.waitPre(); + if ((addr & 0x3000) != 0) { + if ((addr & 0x2000) == 0) this.B.write(r, value >> 8); + if ((addr & 0x1000) == 0) this.A.write(r, value & 0xff); + } + this.waitPost(); + } + + this.store32 = function (addr, value) { + this.store16(addr, value >>> 16); + this.store16(addr + 2, value & 0xffff); + } +} diff --git a/sae/config.js b/sae/config.js new file mode 100644 index 0000000..c501f38 --- /dev/null +++ b/sae/config.js @@ -0,0 +1,188 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Config() { + this.init = false; + + this.cpu = { + model: 68000, + speed: 0, /* 0 if cpu.exact */ + exact: false, + compatible: true + }; + this.blitter = { + exact: false, /* do not change */ + immediate: false, /* false if blitter.exact */ + waiting: 1 /* 0 if cpu.exact or blitter.immediate */ + }; + this.chipset = { + type: 0, + agnus: 0, + agnus_rev: 0, + denise: 0, + paula: 0, + colLevel: 0 + }; + this.ram = { + chip: { + size: 0 + }, + slow: { + size: 0, + }, + fast: { + size: 0, + } + }; + this.rom = { + size: 0, + data: null + }; + this.ext = { + addr: 0, + size: 0, + data: null + }; + this.floppy = { + drive:[{ + type: 0, + name: null, + data: null + }, { + type: 0, + name: null, + data: null + }, { + type: 0, + name: null, + data: null + }, { + type: 0, + name: null, + data: null + }], + speed:0 + }; + this.video = { + enabled: false, + scale: false, + ntsc: false, + skip: false, + id: '' + }; + this.audio = { + enabled: false, + mode:0, + channels: 0, + rate: 0 + }; + this.ports = [{ + type: 0, + move: 0, + fire: [0,0] + }, { + type: 0, + move: 0, + fire: [0,0] + }]; + this.keyboard = { + enabled: false, + mapShift: false + }; + this.serial = { + enabled: false + }; + this.rtc = { + type: 0 + }; + this.hooks = { + error: null, + power_led: null, + floppy_motor: null, + floppy_step: null, + fps: null, + cpu: null + }; +} + +function configSetDefaults(c) { + c.init = true; + + c.cpu.speed = SAEV_Config_CPU_Speed_Original; + + c.chipset.type = SAEV_Config_Chipset_Type_OCS; + if (c.chipset.type == SAEV_Config_Chipset_Type_OCS) { + c.chipset.agnus = c.video.ntsc ? AGNUS_8370 : AGNUS_8371; + c.chipset.agnus_rev = 0; + c.chipset.denise = DENISE_8362; + } else if (c.chipset.type == SAEV_Config_Chipset_Type_ECS) { + c.chipset.agnus = AGNUS_8372; + c.chipset.agnus_rev = 5; + c.chipset.denise = DENISE_8373; + } + c.chipset.paula = PAULA_8364; + c.chipset.colLevel = SAEV_Config_Chipset_ColLevel_None; + + c.ram.chip.size = SAEV_Config_RAM_Chip_Size_512K; + c.ram.slow.size = SAEV_Config_RAM_Slow_Size_512K; + c.ram.fast.size = SAEV_Config_RAM_Fast_Size_512K; + + c.rom.size = SAEV_Config_ROM_Size_None; + c.rom.data = null; + c.ext.addr = SAEV_Config_EXT_Addr_E0; + c.ext.size = SAEV_Config_EXT_Size_None; + c.ext.data = null; + + c.floppy.drive[0].type = SAEV_Config_Floppy_Type_35_DD; + c.floppy.drive[0].name = null; + c.floppy.drive[0].data = null; + c.floppy.drive[1].type = SAEV_Config_Floppy_Type_35_DD; + c.floppy.drive[1].name = null; + c.floppy.drive[1].data = null; + c.floppy.drive[2].type = SAEV_Config_Floppy_Type_None; + c.floppy.drive[2].name = null; + c.floppy.drive[2].data = null; + c.floppy.drive[3].type = SAEV_Config_Floppy_Type_None; + c.floppy.drive[3].name = null; + c.floppy.drive[3].data = null; + c.floppy.speed = SAEV_Config_Floppy_Speed_Original; + + c.video.enabled = true; + c.video.scale = false; + c.video.ntsc = false; + c.video.skip = true; + c.video.id = 'video'; + + c.audio.enabled = true; + c.audio.mode = SAEV_Config_Audio_Mode_Play_Best; + c.audio.channels = SAEV_Config_Audio_Channels_Stereo; + c.audio.rate = SAEV_Config_Audio_Rate_44100; + + c.ports[0].type = SAEV_Config_Ports_Type_Mouse; + c.ports[0].move = SAEV_Config_Ports_Move_WASD; + c.ports[0].fire[0] = 49; + c.ports[0].fire[1] = 50; + c.ports[1].type = SAEV_Config_Ports_Type_Joy1; + c.ports[1].move = SAEV_Config_Ports_Move_Arrows; + c.ports[1].fire[0] = 16; + c.ports[1].fire[1] = 17; + + c.keyboard.enabled = true; + c.keyboard.mapShift = false; + + c.rtc.type = 1 ? SAEV_Config_RTC_Type_MSM6242B : SAEV_Config_RTC_Type_RF5C01A; + + c.hooks.error = function(err, msg) {} + c.hooks.power_led = function(on) {} + c.hooks.floppy_motor = function(unit, on) {} + c.hooks.floppy_step = function(unit, cyl) {} + c.hooks.fps = function(fps) {} + c.hooks.cpu = function(usage) {} +} diff --git a/sae/constants.js b/sae/constants.js new file mode 100644 index 0000000..56ce764 --- /dev/null +++ b/sae/constants.js @@ -0,0 +1,317 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +const SAEV_Version = 0; +const SAEV_Revision = 7; +const SAEV_Revision_Sub = 0; + +/*-----------------------------------------------------------------------*/ + +const SAEV_Null = null; + +const SAEV_True = true; +const SAEV_False = false; + +/*-----------------------------------------------------------------------*/ +/* cpu */ + +const SAEV_Config_CPU_Speed_Maximum = -1; +const SAEV_Config_CPU_Speed_Original = 0; + +/*-----------------------------------------------------------------------*/ +/* chipset */ + +const SAEV_Config_Chipset_Type_OCS = 1; +const SAEV_Config_Chipset_Type_ECS = 2; + +const SAEV_Config_Chipset_ColLevel_None = 0; +const SAEV_Config_Chipset_ColLevel_Sprite_Sprite = 1; +const SAEV_Config_Chipset_ColLevel_Sprite_Playfield = 2; + +/*-----------------------------------------------------------------------*/ +/* ram */ + +const SAEV_Config_RAM_Chip_Size_256K = 1; +const SAEV_Config_RAM_Chip_Size_512K = 2; +const SAEV_Config_RAM_Chip_Size_1M = 3; +const SAEV_Config_RAM_Chip_Size_2M = 4; + +const SAEV_Config_RAM_Slow_Size_None = 0; +const SAEV_Config_RAM_Slow_Size_256K = 1; +const SAEV_Config_RAM_Slow_Size_512K = 2; +const SAEV_Config_RAM_Slow_Size_1M = 3; +const SAEV_Config_RAM_Slow_Size_1536K = 4; + +const SAEV_Config_RAM_Fast_Size_None = 0; +const SAEV_Config_RAM_Fast_Size_512K = 1; +const SAEV_Config_RAM_Fast_Size_1M = 2; +const SAEV_Config_RAM_Fast_Size_2M = 3; +const SAEV_Config_RAM_Fast_Size_4M = 4; +const SAEV_Config_RAM_Fast_Size_8M = 5; + +/*-----------------------------------------------------------------------*/ +/* rom, ext */ + +const SAEV_Config_ROM_Size_None = 0; +const SAEV_Config_ROM_Size_256K = 1; +const SAEV_Config_ROM_Size_512K = 2; + +const SAEV_Config_EXT_Size_None = 0; +const SAEV_Config_EXT_Size_256K = 1; +const SAEV_Config_EXT_Size_512K = 2; + +const SAEV_Config_EXT_Addr_A0 = 1; +const SAEV_Config_EXT_Addr_E0 = 2; +const SAEV_Config_EXT_Addr_F0 = 3; + +/*-----------------------------------------------------------------------*/ +/* disk */ + +const SAEV_Config_Floppy_Type_None = 0; +const SAEV_Config_Floppy_Type_35_DD = 1; +const SAEV_Config_Floppy_Type_35_HD = 2; +const SAEV_Config_Floppy_Type_525_SD = 3; + +const SAEV_Config_Floppy_Speed_Turbo = 0; +const SAEV_Config_Floppy_Speed_Original = 100; + +/*-----------------------------------------------------------------------*/ +/* audio */ + +const SAEV_Config_Audio_Mode_Emul = 0; +const SAEV_Config_Audio_Mode_Play = 1; +const SAEV_Config_Audio_Mode_Play_Best = 2; + +const SAEV_Config_Audio_Channels_Mono = 1; +const SAEV_Config_Audio_Channels_Stereo = 2; + +const SAEV_Config_Audio_Rate_11025 = 1; +const SAEV_Config_Audio_Rate_22050 = 2; +const SAEV_Config_Audio_Rate_44100 = 3; +const SAEV_Config_Audio_Rate_48000 = 4; + +/*-----------------------------------------------------------------------*/ +/* input */ + +const SAEV_Config_Ports_Type_None = 0; +const SAEV_Config_Ports_Type_Mouse = 1; +const SAEV_Config_Ports_Type_Joy0 = 2; +const SAEV_Config_Ports_Type_Joy1 = 3; + +const SAEV_Config_Ports_Move_None = 0; +const SAEV_Config_Ports_Move_Arrows = 1; +const SAEV_Config_Ports_Move_Numpad = 2; +const SAEV_Config_Ports_Move_WASD = 3; + +const SAEV_Config_Ports_Fire_None = 0; + +/*-----------------------------------------------------------------------*/ +/* rtc */ + +const SAEV_Config_RTC_Type_None = 0; +const SAEV_Config_RTC_Type_MSM6242B = 1; +const SAEV_Config_RTC_Type_RF5C01A = 2; + +/*-----------------------------------------------------------------------*/ +/* erros */ + +const SAEE_None = 0; + +const SAEE_CPU_Internal = 1; +const SAEE_CPU_68020_Required = 2; + +const SAEE_Disk_File_Too_Big = 3; + +const SAEE_Video_Shader_Error = 4; +const SAEE_Video_ID_Not_Found = 5; +const SAEE_Video_Canvas_Not_Supported = 6; +const SAEE_Video_WebGL_Not_Avail = 7; + +const SAEE_Audio_WebAudio_Not_Avail = 8; + +/*-----------------------------------------------------------------------*/ +/* methods */ + +const SAEM_Init = 1; +const SAEM_Start = 2; +const SAEM_Stop = 3; +const SAEM_Pause = 4; +const SAEM_Reset = 5; +const SAEM_Insert = 6; +const SAEM_Eject = 7; + +/*-----------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* debug */ + +var BUG = null; + +/*-----------------------------------------------------------------------*/ +/* amiga */ + +var AMIGA = null; + +const CMD_STOP = 0; +const CMD_CYCLE = 1; +const CMD_PAUSE = 2; +const CMD_IDLE = 3; + +/*-----------------------------------------------------------------------*/ +/* events */ + +const EV_CIA = 0; +const EV_AUDIO = 1; +const EV_MISC = 2; +const EV_HSYNC = 3; +const EV_MAX = 4; +//const EV_COPPER = ; +//const EV_BLITTER = ; +//const EV_RENDER = ; + +const EV2_BLITTER = 0; +const EV2_DISK = 1; +const EV2_MISC = 2; +const EV2_MAX = 12; + +const CYCLE_UNIT = 512; +const CYCLE_UNIT_INV = 1.0 / CYCLE_UNIT; /* mul is always faster than div */ + +const CYCLE_MAX = 0xffffffff * CYCLE_UNIT; + +/*-----------------------------------------------------------------------*/ +/* cia */ + +const CIA_A = 1; +const CIA_B = 2; + +const ECLOCK_DATA_CYCLE = 4; +const ECLOCK_WAIT_CYCLE = 6; + +const DIV10 = ((ECLOCK_DATA_CYCLE + ECLOCK_WAIT_CYCLE) * CYCLE_UNIT) >> 1; + +const CIA_RETHINK_DELAY = (CYCLE_UNIT << 1) + (CYCLE_UNIT >> 1); + +/*-----------------------------------------------------------------------*/ +/* config */ + +//OCS +const AGNUS_8361 = 1; /* Amiga 1000 NTSC */ +const AGNUS_8367 = 2; /* Amiga 1000 PAL */ +const AGNUS_8370 = 3; /* OCS NTSC */ +const AGNUS_8371 = 4; /* OCS PAL */ +//ECS +const AGNUS_8372 = 5; /* ECS */ +//AGA +const AGNUS_8374 = 6; /* AGA */ + +const DENISE_8362 = 1; /* OCS */ +const DENISE_8373 = 2; /* ECS */ + +const PAULA_8364 = 1; + +/*-----------------------------------------------------------------------*/ +/* cpu */ + +const SPCFLAG_STOP = 2; +const SPCFLAG_COPPER = 4; +const SPCFLAG_INT = 8; +//const SPCFLAG_BRK = 16; +const SPCFLAG_TRACE = 64; +const SPCFLAG_DOTRACE = 128; +const SPCFLAG_DOINT = 256; +const SPCFLAG_BLTNASTY = 512; +//const SPCFLAG_EXEC = 1024; +//const SPCFLAG_ACTION_REPLAY = 2048; +//const SPCFLAG_TRAP = 4096; + +/*-----------------------------------------------------------------------*/ +/* amiga */ + +const INTF_TBE = 1 << 0; +const INTF_DSKBLK = 1 << 1; +const INTF_PORTS = 1 << 3; +const INTF_COPER = 1 << 4; +const INTF_VERTB = 1 << 5; +const INTF_BLIT = 1 << 6; +const INTF_AUD0 = 1 << 7; +const INTF_AUD1 = 1 << 8; +const INTF_AUD2 = 1 << 9; +const INTF_AUD3 = 1 << 10; +const INTF_RBF = 1 << 11; +const INTF_DSKSYN = 1 << 12; +const INTF_EXTER = 1 << 13; +const INTF_INTEN = 1 << 14; +const INTF_SETCLR = 1 << 15; + +const INT_DSKBLK = INTF_SETCLR | INTF_DSKBLK; +const INT_VERTB = INTF_SETCLR | INTF_VERTB; +const INT_BLIT = INTF_SETCLR | INTF_BLIT; +const INT_DSKSYN = INTF_SETCLR | INTF_DSKSYN; + +const DMAF_AUD0EN = 1 << 0; +const DMAF_AUD1EN = 1 << 1; +const DMAF_AUD2EN = 1 << 2; +const DMAF_AUD3EN = 1 << 3; +const DMAF_DSKEN = 1 << 4; +const DMAF_SPREN = 1 << 5; +const DMAF_BLTEN = 1 << 6; +const DMAF_COPEN = 1 << 7; +const DMAF_BPLEN = 1 << 8; +const DMAF_DMAEN = 1 << 9; +const DMAF_BLTPRI = 1 << 10; +const DMAF_BZERO = 1 << 13; +const DMAF_BBUSY = 1 << 14; +const DMAF_SETCLR = 1 << 15; + +const INT_PROCESSING_DELAY = 3 * CYCLE_UNIT; + +/*-----------------------------------------------------------------------*/ +/* copper */ + +/*const COP_STOP = 0; +const COP_READ = 1; +const COP_MOVE = 2; +const COP_WAIT = 3; +const COP_SKIP = 4;*/ + +/*-----------------------------------------------------------------------*/ +/* blitter */ + +/*const BLT_STOP = 0; +const BLT_INIT = 1; +const BLT_READ = 2; +const BLT_WORK = 3; +const BLT_WRITE = 4; +const BLT_NEXT = 5;*/ + +const BLT_done = 0; +const BLT_init = 1; +const BLT_read = 2; +const BLT_work = 3; +const BLT_write = 4; +const BLT_next = 5; + +/*-----------------------------------------------------------------------*/ +/* video */ + +const VIDEO_WIDTH = 720; /* == 360*2 */ +const VIDEO_HEIGHT = 576; /* == 288*2 */ + +/*-----------------------------------------------------------------------*/ +/* playfield */ + +const MAX_FRAMESKIP_COUNT = 4; + +/*-----------------------------------------------------------------------*/ +/* audio */ + +const PERIOD_MIN = 4; +const PERIOD_MIN_NONCE = 60; +const PERIOD_MAX = 0xffffffff; diff --git a/sae/copper.js b/sae/copper.js new file mode 100644 index 0000000..2bf28e3 --- /dev/null +++ b/sae/copper.js @@ -0,0 +1,615 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +//copper_states +const COP_stop = 0; +const COP_waitforever = 1; +const COP_read1 = 2; +const COP_read2 = 3; +const COP_bltwait = 4; +const COP_wait_in2 = 5; +const COP_skip_in2 = 6; +const COP_wait1 = 7; +const COP_wait = 8; +const COP_skip1 = 9; +const COP_strobe_delay1 = 10; +const COP_strobe_delay2 = 11; +const COP_strobe_delay1x = 12; +const COP_strobe_delay2x = 13; +const COP_strobe_extra = 14; +const COP_start_delay = 15; + +function Copper() { + this.cop1lc = 0; + this.cop2lc = 0, + this.copcon = 0; + this.enabled_thisline = false; + this.access = false; + + var cop_state = { + /* The current instruction words. */ + i1:0, i2:0, + saved_i1:0, saved_i2:0, + state:0, state_prev:0, + /* Instruction pointer. */ + ip:0, saved_ip:0, + hpos:0, vpos:0, + ignore_next:0, + vcmp:0, hcmp:0, + + strobe:0, /* COPJMP1 / COPJMP2 accessed */ + last_write:0, last_write_hpos:0, + moveaddr:0, movedata:0, movedelay:0 + }; + var last_copper_hpos = 0; + + + this.reset = function () { + this.copcon = 0; + cop_state.state = COP_stop; + } + + this.reset2 = function () { + cop_state.hpos = 0; + cop_state.last_write = 0; + this.compute_spcflag_copper(AMIGA.events.maxhpos); + } + + this.COPCON = function (v) { + this.copcon = v; + } + this.COP1LCH = function (v) { + this.cop1lc = ((v << 16) | (this.cop1lc & 0xffff)) >>> 0; + } + this.COP1LCL = function (v) { + this.cop1lc = ((this.cop1lc & 0xffff0000) | (v & 0xfffe)) >>> 0; + } + this.COP2LCH = function (v) { + this.cop2lc = ((v << 16) | (this.cop2lc & 0xffff)) >>> 0; + } + this.COP2LCL = function (v) { + this.cop2lc = ((this.cop2lc & 0xffff0000) | (v & 0xfffe)) >>> 0; + } + + this.COPJMP = function (num, vblank) { + var oldstrobe = cop_state.strobe; + + //if (AMIGA.dmaen(DMAF_COPEN) && (cop_state.saved_i1 != 0xffff || cop_state.saved_i2 != 0xfffe)) + //BUG.info('COPJMP(%d) vblank without copper ending %08x (%08x %08x) (%08x %08x)', num, cop_state.ip, this.cop1lc, this.cop2lc, cop_state.saved_i1, cop_state.saved_i2); + + clr_special(SPCFLAG_COPPER); + cop_state.ignore_next = 0; + if (!oldstrobe) + cop_state.state_prev = cop_state.state; + + if ((cop_state.state == COP_wait || cop_state.state == COP_waitforever) && !vblank) + cop_state.state = COP_strobe_delay1x; + else + cop_state.state = vblank ? COP_start_delay : (this.access ? COP_strobe_delay1 : COP_strobe_extra); + + //BUG.info('COPJMP(%d) %d', num, cop_state.state); + + cop_state.vpos = AMIGA.events.vpos; + cop_state.hpos = AMIGA.events.hpos() & ~1; + this.enabled_thisline = false; + cop_state.strobe = num; + + if (0) { + this.immediate_copper(num); + return; + } + + if (AMIGA.dmaen(DMAF_COPEN)) + this.compute_spcflag_copper(AMIGA.events.hpos()); + else if (oldstrobe > 0 && oldstrobe != num && cop_state.state_prev == COP_wait) { + /* dma disabled, copper idle and accessed both COPxJMPs -> copper stops! */ + cop_state.state = COP_stop; + //BUG.info('COPJMP(%d) COP_stop'); + } + } + + /*function get_copper_address(copno) { + switch (copno) { + case 1: return this.cop1lc; + case 2: return this.cop2lc; + case -1: return cop_state.ip; + default: return 0; + } + }*/ + + this.test_copper_dangerous = function (address) { + var addr = address & 0x1fe; + if (addr < ((this.copcon & 2) ? ((AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS) ? 0 : 0x40) : 0x80)) { + cop_state.state = COP_stop; + this.enabled_thisline = false; + clr_special(SPCFLAG_COPPER); + return true; + } + return false; + } + + this.immediate_copper = function (num) { + var pos = 0; + var oldpos = 0; + + cop_state.state = COP_stop; + cop_state.vpos = AMIGA.events.vpos; + cop_state.hpos = AMIGA.events.hpos() & ~1; + cop_state.ip = num == 1 ? this.cop1lc : this.cop2lc; + + while (pos < (AMIGA.events.maxvpos << 5)) { + if (oldpos > pos) + pos = oldpos; + if (!AMIGA.dmaen(DMAF_COPEN)) + break; + if (cop_state.ip >= AMIGA.mem.chip.size) + break; + pos++; + oldpos = pos; + cop_state.i1 = AMIGA.mem.load16_chip(cop_state.ip); + cop_state.i2 = AMIGA.mem.load16_chip(cop_state.ip + 2); + cop_state.ip += 4; + if (!(cop_state.i1 & 1)) { // move + cop_state.i1 &= 0x1fe; + if (cop_state.i1 == 0x88) { + cop_state.ip = this.cop1lc; + continue; + } + if (cop_state.i1 == 0x8a) { + cop_state.ip = this.cop2lc; + continue; + } + if (this.test_copper_dangerous(cop_state.i1)) + break; + AMIGA.custom.store16_real(0, cop_state.i1, cop_state.i2, 0); + } else { // wait or skip + if ((cop_state.i1 >> 8) > ((pos >> 5) & 0xff)) + pos = (((pos >> 5) & 0x100) | ((cop_state.i1 >> 8)) << 5) | ((cop_state.i1 & 0xff) >> 3); + if (cop_state.i1 >= 0xffdf && cop_state.i2 == 0xfffe) + break; + } + } + cop_state.state = COP_stop; + clr_special(SPCFLAG_COPPER); + } + + this.copper_cant_read = function (hpos, alloc) { + //BUG.info('copper_cant_read2() hpos %d / %d', hpos, AMIGA.events.maxhpos); + if (hpos + 1 >= AMIGA.events.maxhpos) // first refresh slot + return 1; + if ((hpos == AMIGA.events.maxhpos - 3) && (AMIGA.events.maxhpos & 1) && alloc >= 0) { + //if (alloc) alloc_cycle (hpos, CYCLE_COPPER); + return -1; + } + return AMIGA.playfield.is_bitplane_dma(hpos); + } + + /*this.copper_cant_read = function (hpos, alloc) { + var cant = this.copper_cant_read2(hpos, alloc); + //if (cant && debug_dma) record_dma_event (DMA_EVENT_COPPERWANTED, hpos, vpos); + return cant; + }*/ + + this.custom_store16_copper = function (hpos, addr, value, noget) { + //if (addr == 0x88 || addr == 0x8a) + //BUG.info('custom_store16_copper() addr %08x, value %04x | vpos %d hpos %d %d cvcmp %d chcmp %d chpos %d cvpos %d', addr, value, AMIGA.events.vpos, AMIGA.events.hpos(), hpos, cop_state.vcmp, cop_state.hcmp, cop_state.hpos, cop_state.vpos); + //value = debug_wputpeekdma (0xdff000 + addr, value); + this.access = true; + var v = AMIGA.custom.store16_real(hpos, addr, value, noget); + this.access = false; + return v; + } + + this.dump_copper = function (error, until_hpos) { + BUG.info('\n'); + BUG.info('%s: vpos=%d until_hpos=%d vp=%d', error, AMIGA.events.vpos, until_hpos, AMIGA.events.vpos & (((cop_state.saved_i2 >> 8) & 0x7f) | 0x80)); + BUG.info('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); + BUG.info('cstate=%d ip=%x SPCFLAGS=%x iscline=%d', cop_state.state, cop_state.ip, AMIGA.spcflags, this.enabled_thisline?1:0); + BUG.info('\n'); + } + + // 'emulate' chip internal delays, not the right place but fast and 99.9% programs + // use only copper to write BPLCON1 etc.. (exception is HulkaMania/TSP..) + // this table should be filled with zeros and done somewhere else.. + var customdelay = [ + 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 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 - 0x5e */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 - 0x7e */ + 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, /* 0x80 - 0x9e */ + 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 */ + /* BPLxPTH/BPLxPTL */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16 */ + /* BPLCON0-3,BPLMOD1-2 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16 */ + /* SPRxPTH/SPRxPTL */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 16 */ + /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */ + 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, + /* COLORxx */ + 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, + /* RESERVED */ + 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 + ]; + + /*this.copper_write = function (v) { + this.custom_store16_copper(AMIGA.events.hpos(), v >> 16, v & 0xffff, 0); + }*/ + + this.update_copper = function (until_hpos) { + var vp = AMIGA.events.vpos & (((cop_state.saved_i2 >> 8) & 0x7f) | 0x80); + var c_hpos = cop_state.hpos; + + //BUG.info('update_copper() until_hpos %d, vp %d', until_hpos, vp); + + if (cop_state.state == COP_wait && vp < cop_state.vcmp) { + this.dump_copper('error2', until_hpos); + this.enabled_thisline = false; + cop_state.state = COP_stop; + clr_special(SPCFLAG_COPPER); + return; + } + + if (until_hpos <= last_copper_hpos) + return; + + if (until_hpos > (AMIGA.events.maxhpos & ~1)) + until_hpos = AMIGA.events.maxhpos & ~1; + + for (;;) { + var old_hpos = c_hpos; + var hp; + + if (c_hpos >= until_hpos) + break; + + /* So we know about the fetch state. */ + //decide_line (c_hpos); + //decide_fetch (c_hpos); + + if (cop_state.movedelay > 0) { + cop_state.movedelay--; + if (cop_state.movedelay == 0) { + this.custom_store16_copper(c_hpos, cop_state.moveaddr, cop_state.movedata, 0); + } + } + + if ((c_hpos == AMIGA.events.maxhpos - 3) && (AMIGA.events.maxhpos & 1)) + c_hpos += 1; + else + c_hpos += 2; + + switch (cop_state.state) { + case COP_wait_in2: { + if (this.copper_cant_read(old_hpos, 0)) + continue; + cop_state.state = COP_wait1; + break; + } + case COP_skip_in2: { + if (this.copper_cant_read(old_hpos, 0)) + continue; + cop_state.state = COP_skip1; + break; + } + case COP_strobe_extra: { + // Wait 1 copper cycle doing nothing + cop_state.state = COP_strobe_delay1; + break; + } + case COP_strobe_delay1: { + // First cycle after COPJMP is just like normal first read cycle + // Cycle is used and needs to be free. + if (this.copper_cant_read(old_hpos, 1)) + continue; + cop_state.state = COP_strobe_delay2; + //alloc_cycle(old_hpos, CYCLE_COPPER); + //if (debug_dma) record_dma (0x8c, AMIGA.mem.load16_chip(cop_state.ip), cop_state.ip, old_hpos, vpos, DMARECORD_COPPER); + cop_state.ip += 2; + break; + } + case COP_strobe_delay2: { + // Second cycle after COPJMP. This is the strange one. + // This cycle does not need to be free + // But it still gets allocated by copper if it is free = CPU and blitter can't use it. + /*if (this.copper_cant_read(old_hpos, 1)) { + alloc_cycle (old_hpos, CYCLE_COPPER); + if (debug_dma) record_dma (0x1fe, AMIGA.mem.load16_chip(cop_state.ip), cop_state.ip, old_hpos, vpos, DMARECORD_COPPER); + }*/ + cop_state.state = COP_read1; + // Next cycle finally reads from new pointer + if (cop_state.strobe == 1) + cop_state.ip = this.cop1lc; + else + cop_state.ip = this.cop2lc; + cop_state.strobe = 0; + break; + } + case COP_strobe_delay1x: { + // First cycle after COPJMP and Copper was waiting. This is the buggy one. + // Cycle can be free and copper won't allocate it. + // If Blitter uses this cycle = Copper's address gets copied blitter DMA pointer.. + cop_state.state = COP_strobe_delay2x; + break; + } + case COP_strobe_delay2x: { + // Second cycle fetches following word and tosses it away. Must be free cycle + // but is not allocated, blitter or cpu can still use it. + if (this.copper_cant_read(old_hpos, 1)) + continue; + //cycle_line[old_hpos] |= CYCLE_COPPER_SPECIAL; + //if (debug_dma) record_dma (0x1fe, AMIGA.mem.load16_chip(cop_state.ip), cop_state.ip, old_hpos, vpos, DMARECORD_COPPER); + cop_state.state = COP_read1; + // Next cycle finally reads from new pointer + if (cop_state.strobe == 1) + cop_state.ip = this.cop1lc; + else + cop_state.ip = this.cop2lc; + cop_state.strobe = 0; + break; + } + case COP_start_delay: { + if (this.copper_cant_read(old_hpos, 1)) + continue; + cop_state.state = COP_read1; + //alloc_cycle(old_hpos, CYCLE_COPPER); + //if (debug_dma) record_dma (0x1fe, 0, 0xffffffff, old_hpos, vpos, DMARECORD_COPPER); + cop_state.ip = this.cop1lc; + break; + } + case COP_read1: { + if (this.copper_cant_read(old_hpos, 1)) + continue; + /* workaround for a bug in kick 1.x */ + if (cop_state.ip == 0x00000004 || cop_state.ip == 0x00000676 || cop_state.ip == 0x00c00276) { + //BUG.info('COP_read1() invalid addr $%08x', cop_state.ip); + cop_state.state = COP_stop; + this.enabled_thisline = false; + clr_special(SPCFLAG_COPPER); + return; + } + cop_state.i1 = AMIGA.mem.load16_chip(cop_state.ip); + //alloc_cycle(old_hpos, CYCLE_COPPER); + //if (debug_dma) record_dma (0x8c, cop_state.i1, cop_state.ip, old_hpos, vpos, DMARECORD_COPPER); + cop_state.ip += 2; + cop_state.state = COP_read2; + break; + } + case COP_read2: { + if (this.copper_cant_read(old_hpos, 1)) + continue; + cop_state.i2 = AMIGA.mem.load16_chip(cop_state.ip); + //alloc_cycle(old_hpos, CYCLE_COPPER); + cop_state.ip += 2; + cop_state.saved_i1 = cop_state.i1; + cop_state.saved_i2 = cop_state.i2; + cop_state.saved_ip = cop_state.ip; + + if (cop_state.i1 & 1) { // WAIT or SKIP + cop_state.ignore_next = 0; + if (cop_state.i2 & 1) + cop_state.state = COP_skip_in2; + else + cop_state.state = COP_wait_in2; + //if (debug_dma) record_dma (0x8c, cop_state.i2, cop_state.ip - 2, old_hpos, vpos, DMARECORD_COPPER); + } else { // MOVE + //uaecptr debugip = cop_state.ip; + var reg = cop_state.i1 & 0x1fe; + var data = cop_state.i2; + cop_state.state = COP_read1; + //if (debug_dma) record_dma (reg, data, cop_state.ip - 2, old_hpos, vpos, DMARECORD_COPPER); + this.test_copper_dangerous(reg); + if (!this.enabled_thisline) { + //goto out; // was 'dangerous' register -> copper stopped + cop_state.hpos = c_hpos; + last_copper_hpos = until_hpos; + return; + } + if (cop_state.ignore_next) + reg = 0x1fe; + + cop_state.last_write = reg; + cop_state.last_write_hpos = old_hpos; + if (reg == 0x88) { + cop_state.strobe = 1; + cop_state.state = COP_strobe_delay1; + } else if (reg == 0x8a) { + cop_state.strobe = 2; + cop_state.state = COP_strobe_delay1; + } else { + /*#if 0 + event2_newevent2 (1, (reg << 16) | data, copper_write); + #else*/ + // FIX: all copper writes happen 1 cycle later than CPU writes + if (customdelay[reg >> 1]) { + cop_state.moveaddr = reg; + cop_state.movedata = data; + cop_state.movedelay = customdelay[cop_state.moveaddr >> 1]; + } else { + var hpos2 = old_hpos; + this.custom_store16_copper(hpos2, reg, data, 0); + hpos2++; + //if (reg >= 0x140 && reg < 0x180 && hpos2 >= SPR0_HPOS && hpos2 < SPR0_HPOS + 4 * MAX_SPRITES) do_sprites(hpos2); + } + //#endif + } + //if (debug_copper && !cop_state.ignore_next) record_copper (debugip - 4, old_hpos, vpos); + cop_state.ignore_next = 0; + } + break; + } + case COP_wait1: { + /*#if 0 + if (c_hpos >= (AMIGA.events.maxhpos & ~1) || (c_hpos & 1)) break; + #endif*/ + cop_state.state = COP_wait; + + cop_state.vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8; + cop_state.hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xfe); + + vp = AMIGA.events.vpos & (((cop_state.saved_i2 >> 8) & 0x7f) | 0x80); + + if (cop_state.saved_i1 == 0xffff && cop_state.saved_i2 == 0xfffe) { + cop_state.state = COP_waitforever; + this.enabled_thisline = false; + clr_special(SPCFLAG_COPPER); + //goto out; + cop_state.hpos = c_hpos; + last_copper_hpos = until_hpos; + return; + } + if (vp < cop_state.vcmp) { + this.enabled_thisline = false; + clr_special(SPCFLAG_COPPER); + //goto out; + cop_state.hpos = c_hpos; + last_copper_hpos = until_hpos; + return; + } + } + /* fall through */ + case COP_wait: { + var ch_comp = c_hpos; + if (ch_comp & 1) + ch_comp = 0; + + if (this.copper_cant_read(old_hpos, 0)) + continue; + + hp = ch_comp & (cop_state.saved_i2 & 0xfe); + if (vp == cop_state.vcmp && hp < cop_state.hcmp) + break; + + /* Now we know that the comparisons were successful. We might still have to wait for the blitter though. */ + if ((cop_state.saved_i2 & 0x8000) == 0) { + //decide_blitter (old_hpos); + if (AMIGA.blitter.getState() != BLT_done) { + //We need to wait for the blitter. + cop_state.state = COP_bltwait; + this.enabled_thisline = false; + clr_special(SPCFLAG_COPPER); + //goto out; + cop_state.hpos = c_hpos; + last_copper_hpos = until_hpos; + return; + } /*else { + if (debug_dma) record_dma_event (DMA_EVENT_COPPERWAKE, old_hpos, vp); + }*/ + } + //if (debug_copper) record_copper (cop_state.ip - 4, old_hpos, vpos); + cop_state.state = COP_read1; + break; + } + case COP_skip1: { + var vcmp, hcmp, vp1, hp1; + + if (c_hpos >= (AMIGA.events.maxhpos & ~1) || (c_hpos & 1)) + break; + + if (this.copper_cant_read(old_hpos, 0)) + continue; + + vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8; + hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xfe); + vp1 = AMIGA.events.vpos & (((cop_state.saved_i2 >> 8) & 0x7f) | 0x80); + hp1 = c_hpos & (cop_state.saved_i2 & 0xfe); + + if ((vp1 > vcmp || (vp1 == vcmp && hp1 >= hcmp)) && ((cop_state.saved_i2 & 0x8000) != 0 || AMIGA.blitter.getState() == BLT_done)) + cop_state.ignore_next = 1; + + cop_state.state = COP_read1; + //if (debug_copper) record_copper (cop_state.ip - 4, old_hpos, vpos); + break; + } + } + } + + //out: + cop_state.hpos = c_hpos; + last_copper_hpos = until_hpos; + } + + this.compute_spcflag_copper = function (hpos) { + //BUG.info('compute_spcflag_copper() hpos %d', hpos); + var wasenabled = this.enabled_thisline; + + this.enabled_thisline = false; + clr_special(SPCFLAG_COPPER); + if (!AMIGA.dmaen(DMAF_COPEN) || cop_state.state == COP_stop || cop_state.state == COP_waitforever || cop_state.state == COP_bltwait) + return; + + if (cop_state.state == COP_wait) { + var vp = AMIGA.events.vpos & (((cop_state.saved_i2 >> 8) & 0x7f) | 0x80); + + if (vp < cop_state.vcmp) + return; + } + // do not use past cycles if starting for the first time in this line + // (write to DMACON for example) hpos+1 for long lines + if (!wasenabled && cop_state.hpos < hpos && hpos < AMIGA.events.maxhpos) { + hpos = (hpos + 2) & ~1; + if (hpos > 226) + hpos = 226; + cop_state.hpos = hpos; + } + + // if COPJMPx was written while DMA was disabled, advance to next state, + // COP_strobe_extra is single cycle only and does not need free bus. + // (copper state emulation does not run if DMA is disabled) + if (!wasenabled && cop_state.state == COP_strobe_extra) + cop_state.state = COP_strobe_delay1; + + this.enabled_thisline = true; + set_special(SPCFLAG_COPPER); + } + + this.blitter_done_notify = function (hpos) { + if (cop_state.state != COP_bltwait) + return; + + //BUG.info('blitter_done_notify() hpos %d', hpos); + + var vp = AMIGA.events.vpos; + hpos += 3; + hpos &= ~1; + if (hpos >= AMIGA.events.maxhpos) { + hpos -= AMIGA.events.maxhpos; + vp++; + } + cop_state.hpos = hpos; + cop_state.vpos = vp; + cop_state.state = COP_read1; + //if (debug_dma) record_dma_event (DMA_EVENT_COPPERWAKE, hpos, vp); + + if (AMIGA.dmaen(DMAF_COPEN) && vp == AMIGA.events.vpos) { + this.enabled_thisline = true; + set_special(SPCFLAG_COPPER); + } + } + + this.cycle = function () { + this.update_copper(AMIGA.events.hpos()); + } + + this.sync_copper_with_cpu = function (hpos, do_schedule) { + /* Need to let the copper advance to the current position. */ + if (this.enabled_thisline) + this.update_copper(hpos); + } + + this.check = function (n) { + if (cop_state.state == COP_wait) { + var vp = AMIGA.events.vpos & (((cop_state.saved_i2 >> 8) & 0x7f) | 0x80); + if (vp < cop_state.vcmp) { + if (this.enabled_thisline) + BUG.info('COPPER BUG %d: vp=%d vpos=%d vcmp=%d thisline=%d', n, vp, AMIGA.events.vpos, cop_state.vcmp, this.enabled_thisline?1:0); + } + } + } +} \ No newline at end of file diff --git a/sae/cpu.js b/sae/cpu.js new file mode 100644 index 0000000..a134762 --- /dev/null +++ b/sae/cpu.js @@ -0,0 +1,5232 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +************************************************************************** +* +* TODO: +* - Faster versions of ASx/LSx/ROx/ROXx and xBCD +* - Complete exception 2/3 handling +* - Explode ADDQ,SUBQ +* +* Notes: +* - Based on M68000PRM.pdf +* - Written from scratch. +* +*************************************************************************/ + +function CPU() { + const M_rdd = 1; /* Register Direct Data */ + const M_rda = 2; /* Register Direct Address */ + const M_ria = 3; /* Register Indirect Address */ + const M_ripo = 4; /* Register Indirect Address with Postincrement */ + const M_ripr = 5; /* Register Indirect Address with Predecrement */ + const M_rid = 6; /* Register Indirect Address with Displacement */ + const M_rii = 7; /* Address Register Indirect, with Index (8-Bit Displacement) */ + const M_pcid = 8; /* Program Counter Indirect with Displacement */ + const M_pcii = 9; /* Program Counter Indirect with Index (8-Bit Displacement) */ + const M_absw = 10; /* Absolute Data Addressing */ + const M_absl = 11; /* Absolute Data Addressing */ + const M_imm = 12; /* Immediate Data */ + const M_list = 16; /* Ax,Dx-list for easy MOVEM debug */ + + const T_RD = 1; /* Register Data */ + const T_RA = 2; /* Register Address */ + const T_AD = 3; /* Address */ + const T_IM = 4; /* Immediate */ + + const ccNames = ['T', 'F', 'HI', 'LS', 'CC', 'CS', 'NE', 'EQ', 'VC', 'VS', 'PL', 'MI', 'GE', 'LT', 'GT', 'LE']; + + /* Effective Address */ + function effAddr(m, r) { + this.m = m; /* Mode M_ */ + this.t = 0; /* Type T_ */ + this.r = r; /* Register An/Dn */ + this.a = 0; /* Address */ + this.c = 0; /* Cycles */ + } + + /* Instruction Condition */ + function iCon(cc, dp, dr) { + this.cc = cc; /* Condition Code */ + this.dp = dp; /* Displacement */ + this.dr = dr; /* Data Register for DBcc */ + } + + /* Instruction Paramenter */ + function iPar() { + this.z; /* size B,W,L */ + /* Filled on demand + this.s = new effAddr(); + this.d = new effAddr(); + this.c = new iCon(); + this.ms = 0; + this.mz = 0; + this.cyc = 0;*/ + } + + /* Instruction Definition */ + function iDef() { + this.op; /* OP-code */ + this.pr; /* Privileged */ + this.mn; /* Mnemonic */ + this.f; /* Function */ + this.p = new iPar(); + } + + /* Exception 2/3 error */ + function Exception23(num) { + this.num = num; + } + Exception23.prototype = new Error; + + /*-----------------------------------------------------------------------*/ + + const undef = false; /* use undef */ + + var regs = { + d: [0, 0, 0, 0, 0, 0, 0, 0], /* Dn */ + a: [0, 0, 0, 0, 0, 0, 0, 0], /* An */ + /* Status Register (SR) */ + t: false, + s: false, + intmask: 0, + /* Condition Code Register (CCR) */ + x: false, + n: false, + z: false, + v: false, + c: false, + usp: 0, /* User Stack Ptr (USP) */ + isp: 0, /* Interrupt Stack Ptr (ISP) */ + pc: 0, /* Program Counter (PC) */ + /* Interrupts */ + ipl:0, + ipl_pin:0, + stopped:true + }; + var fault = { + op: 0, + pc: 0, + ad: 0, + ia: false + }; + var iTab = null; + var doDiss = 0; + + /*-----------------------------------------------------------------------*/ + + this.setup = function() { + if (iTab === null) { + BUG.say('cpu.setup() no instruction table, generating...'); + if (!mkiTab()) + Fatal(SAEE_CPU_Internal, 'cpu.setup() error generating function table'); + } else + BUG.say('cpu.setup() instruction table is cached'); + } + + this.reset = function (addr) { + for (var i = 0; i < 8; i++) + regs.d[i] = regs.a[i] = 0; + + regs.t = false; + regs.s = true; + regs.intmask = 7; + regs.x = regs.n = regs.z = regs.v = regs.c = false; + regs.usp = 0; + regs.isp = 0; + regs.a[7] = AMIGA.mem.load32(addr); + regs.pc = AMIGA.mem.load32(addr + 4); + regs.ipl = regs.ipl_pin = 0; + regs.stopped = false; + + doDiss = 0; + + BUG.say(sprintf('cpu.reset() addr 0x%08x, A7 0x%08x, PC 0x%08x', addr, regs.a[7], regs.pc)); + } + + /*-----------------------------------------------------------------------*/ + + function szChr(z) { + switch (z) { + case 0: return 'S'; + case 1: return 'B'; + case 2: return 'W'; + case 4: return 'L'; + default: + Fatal(SAEE_CPU_Internal, 'cpu.szChr() invalid size'); + } + } + + function regsStr(v, inv) { + var out = ''; + for (var i = 0; i < 16; i++) { + if (v & (1 << (inv ? 15-i : i))) { + if (i < 8) { + out += 'D'+i+' '; + } else { + out += 'A'+(i-8)+' '; + } + } + } + return out; + } + + function castByte(v) { + return (v & 0x80) ? (v - 0x100) : v; + } + function castWord(v) { + return (v & 0x8000) ? (v - 0x10000) : v; + } + function castLong(v) { + return (v & 0x80000000) ? (v - 0x100000000) : v; + } + + function extByteToWord(v) { + return (v & 0x80) ? (0xff00 | v) : v; + } + function extByte(v) { + return (v & 0x80) ? ((0xffffff00 | v) >>> 0) : v; + } + function extWord(v) { + return (v & 0x8000) ? ((0xffff0000 | v) >>> 0) : v; + } + + function add32(a, b) { + var r = a + b; + return r > 0xffffffff ? r - 0x100000000 : r; + } + function addAuto(a, b, z) { + var r = a + b; + switch (z) { + case 1: return r > 0xff ? r - 0x100 : r; + case 2: return r > 0xffff ? r - 0x10000 : r; + case 4: return r > 0xffffffff ? r - 0x100000000 : r; + default: + Fatal(SAEE_CPU_Internal, 'cpu.addAuto() invalid size'); + } + } + function sub32(a, b) { + var r = a - b; + return r < 0 ? r + 0x100000000 : r; + } + function subAuto(a, b, z) { + var r = a - b; + switch (z) { + case 1: return r < 0 ? r + 0x100 : r; + case 2: return r < 0 ? r + 0x10000 : r; + case 4: return r < 0 ? r + 0x100000000 : r; + default: + Fatal(SAEE_CPU_Internal, 'cpu.subAuto() invalid size'); + } + } + + function nextIWord() { + var r = AMIGA.mem.load16(regs.pc); + regs.pc += 2; + return r; + } + function nextILong() { + var r = AMIGA.mem.load32(regs.pc); + regs.pc += 4; + return r; + } + + //var scale = 1 << ((ext & 0x600) >> 9); if (scale != 1) alert('exII() scale '+scale); + function exII(base) { + var ext = nextIWord(); + if (ext & 0x100) + Fatal(SAEE_CPU_68020_Required, 'cpu.exII() Full extension index (not a 68000 program)'); + else { + var disp = extByte(ext & 0xff); + var r = (ext & 0x7000) >> 12; + var reg = (ext & 0x8000) ? regs.a[r] : regs.d[r]; + if (!(ext & 0x800)) reg = extWord(reg & 0xffff); + return add32(add32(base, disp), reg); + } + } + + function exEA(ea, z) { + var dp; + + switch (ea.m) { + case M_rdd: + ea.a = ea.r; + ea.t = T_RD; + //ea.c = 0; + break; + case M_rda: + ea.a = ea.r; + ea.t = T_RA; + //ea.c = 0; + break; + case M_ria: + ea.a = regs.a[ea.r]; + ea.t = T_AD; + //ea.c = z == 4 ? 8 : 4; + break; + case M_ripo: + ea.a = regs.a[ea.r]; + ea.t = T_AD; + //ea.c = z == 4 ? 8 : 4; + //regs.a[ea.r] = add32(regs.a[ea.r], z); + regs.a[ea.r] += z; + if (regs.a[ea.r] > 0xffffffff) { + BUG.say(sprintf('exEA() M_ripo A%d > 2^32 ($%x)', ea.r, regs.a[ea.r])); + regs.a[ea.r] -= 0x100000000; + //AMIGA.cpu.diss(fault.pc, 1); + //AMIGA.cpu.dump(); + //exception2(regs.a[ea.r], 0); + } + break; + case M_ripr: + //regs.a[ea.r] = sub32(regs.a[ea.r], z); + regs.a[ea.r] -= z; + if (regs.a[ea.r] < 0) { + BUG.say(sprintf('exEA() M_ripr A%d < 0 ($%x)', ea.r, regs.a[ea.r])); + regs.a[ea.r] += 0x100000000; + //AMIGA.cpu.diss(fault.pc, 1); + //AMIGA.cpu.dump(); + //exception2(regs.a[ea.r], 0); + } + ea.a = regs.a[ea.r]; + ea.t = T_AD; + //ea.c = z == 4 ? 10 : 6; + break; + case M_rid: + dp = (nextIWord()); + ea.a = add32(regs.a[ea.r], extWord(dp)); + ea.t = T_AD; + //ea.c = z == 4 ? 12 : 8; + break; + case M_rii: + ea.a = exII(regs.a[ea.r]); + ea.t = T_AD; + //ea.c = z == 4 ? 14 : 10; + break; + case M_pcid: + dp = extWord(nextIWord()); + ea.a = add32(regs.pc - 2, dp); + ea.t = T_AD; + //ea.c = z == 4 ? 12 : 8; + break; + case M_pcii: + ea.a = exII(regs.pc); + ea.t = T_AD; + //ea.c = z == 4 ? 14 : 10; + break; + case M_absw: + ea.a = extWord(nextIWord()); + ea.t = T_AD; + //ea.c = z == 4 ? 12 : 8; + break; + case M_absl: + ea.a = nextILong(); + ea.t = T_AD; + //ea.c = z == 4 ? 16 : 12; + break; + case M_imm: { + if (ea.r == -1) { + switch (z) { + case 1: ea.a = nextIWord() & 0xff; break; + case 2: ea.a = nextIWord(); break; + case 4: ea.a = nextILong(); break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.exEA() invalid size'); + } + } else + ea.a = ea.r; + ea.t = T_IM; + //ea.c = z == 4 ? 8 : 4; + break; + } + default: + Fatal(SAEE_CPU_Internal, 'cpu.exEA() invalid mode (' + ea.m + ')'); + } + return ea; + } + + function exEAM(ea) { /* MOVEM */ + var dp; + + switch (ea.m) { + case M_ria: + case M_ripo: + case M_ripr: + ea.a = regs.a[ea.r]; + ea.t = T_AD; + break; + case M_rid: + dp = extWord(nextIWord()); + ea.a = add32(regs.a[ea.r], dp); + ea.t = T_AD; + break; + case M_rii: + ea.a = exII(regs.a[ea.r]); + ea.t = T_AD; + break; + case M_pcid: + dp = extWord(nextIWord()); + ea.a = add32(regs.pc - 2, dp); + ea.t = T_AD; + break; + case M_pcii: + ea.a = exII(regs.pc); + ea.t = T_AD; + break; + case M_absw: + ea.a = extWord(nextIWord()); + ea.t = T_AD; + break; + case M_absl: + ea.a = nextILong(); + ea.t = T_AD; + break; + case M_list: /* M_imm */ + ea.a = nextIWord(); + ea.t = T_IM; + break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.exEAM() invalid mode (' + ea.m + ')'); + } + ea.c = 0; + return ea; + } + + function ldEA(ea, z) { + switch (ea.t) { + case T_RD: { + switch (z) { + case 1: return regs.d[ea.a] & 0xff; + case 2: return regs.d[ea.a] & 0xffff; + case 4: return regs.d[ea.a]; + default: + Fatal(SAEE_CPU_Internal, 'cpu.ldEA() T_RD invalid size'); + } + } + case T_RA: { + switch (z) { + case 2: return regs.a[ea.a] & 0xffff; + case 4: return regs.a[ea.a]; + default: + Fatal(SAEE_CPU_Internal, 'cpu.ldEA() T_RA invalid size'); + } + } + case T_AD: { + /* The USP must not be byte-aligned */ + if (ea.m == M_ripo && ea.r == 7 && z == 1) { + //BUG.say(sprintf('ldEA() USP ADDRESS ERROR A7 $%08x', regs.a[7])); + regs.a[7]++; + return AMIGA.mem.load16(regs.a[7] - 2) >> 8; + } + if (ea.a > 0xffffff) { //&& ea.m != M_absl) { + //BUG.say(sprintf('ldEA() BUS ERROR, $%08x > 24bit, reducing address to $%08x', ea.a, ea.a & 0xffffff)); + ea.a &= 0xffffff; + } + if ((ea.a & 1) && z != 1) { + BUG.say(sprintf('ldEA() ADDRESS ERROR $%08x, pc $%08x', ea.a, fault.pc)); + //AMIGA.cpu.diss(fault.pc-8, 20); + //AMIGA.cpu.dump(); + exception3(ea.a, 1); + } + switch (z) { + case 1: return AMIGA.mem.load8(ea.a); + case 2: return AMIGA.mem.load16(ea.a); + case 4: return AMIGA.mem.load32(ea.a); + default: + Fatal(SAEE_CPU_Internal, 'cpu.ldEA() T_AD invalid size'); + } + } + case T_IM: + return ea.a; + default: + Fatal(SAEE_CPU_Internal, 'cpu.ldEA() invalid type (' + ea.t + ')'); + } + } + + function stEA(ea, z, v) { + switch (ea.t) { + case T_RD: + switch (z) { + case 1: regs.d[ea.a] = ((regs.d[ea.a] & 0xffffff00) | v) >>> 0; break; + case 2: regs.d[ea.a] = ((regs.d[ea.a] & 0xffff0000) | v) >>> 0; break; + case 4: regs.d[ea.a] = v; break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.stEA() invalid size'); + } + break; + case T_RA: + regs.a[ea.a] = v; + break; + case T_AD: { + /* The USP must not be byte-aligned */ + if (ea.m == M_ripr && ea.r == 7 && z == 1) { + //BUG.say(sprintf('stEA() USP ADDRESS ERROR A7 $%08x', regs.a[7])); + AMIGA.mem.store16(--regs.a[7], v << 8); + return; + } + if (ea.a > 0xffffff) { //&& ea.m != M_absl) { + //BUG.say(sprintf('stEA() BUS ERROR, $%08x > 24bit, reducing address to $%08x', ea.a, ea.a & 0xffffff)); + ea.a &= 0xffffff; + } + if ((ea.a & 1) && z != 1) { + BUG.say(sprintf('stEA() ADDRESS ERROR $%08x, pc $%08x', ea.a, fault.pc)); + //AMIGA.cpu.diss(fault.pc-8, 20); + //AMIGA.cpu.dump(); + exception3(ea.a, 1); + } + switch (z) { + case 1: AMIGA.mem.store8(ea.a, v); break; + case 2: AMIGA.mem.store16(ea.a, v); break; + case 4: AMIGA.mem.store32(ea.a, v); break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.stEA() invalid size'); + } + break; + } + default: + Fatal(SAEE_CPU_Internal, 'cpu.stEA() invalid type (' + ea.t + ')'); + } + } + + function ccTrue(cc) { + switch (cc) { + case 0: return true; //T + case 1: return false; //F + case 2: return !regs.c && !regs.z; //HI + case 3: return regs.c || regs.z; //LS + case 4: return !regs.c; //CC + case 5: return regs.c; //CS + case 6: return !regs.z; //NE + case 7: return regs.z; //EQ + case 8: return !regs.v; //VC + case 9: return regs.v; //VV + case 10: return !regs.n; //PL + case 11: return regs.n; //MI + case 12: return regs.n == regs.v; //GE + case 13: return regs.n != regs.v; //LT + case 14: return !regs.z && (regs.n == regs.v); //GT + case 15: return regs.z || (regs.n != regs.v); //LE + default: + Fatal(SAEE_CPU_Internal, 'cpu.ccTrue() invalid condition code (' + cc + ')'); + } + } + + /*switch (z) { + case 1: if (S > 0xff || D > 0xff || R > 0xff) alert('fadd 8'); break; + case 2: if (S > 0xffff || D > 0xffff || R > 0xffff) alert('fadd 16'); break; + }*/ + function flgAdd(S, D, R, z, isADDX) /* ADD, ADDI, ADDQ, ADDX */ + { + var Sm, Dm, Rm; + + switch (z) { + case 1: + Sm = (S & 0x80) != 0; + Dm = (D & 0x80) != 0; + Rm = (R & 0x80) != 0; + break; + case 2: + Sm = (S & 0x8000) != 0; + Dm = (D & 0x8000) != 0; + Rm = (R & 0x8000) != 0; + break; + case 4: + Sm = (S & 0x80000000) != 0; + Dm = (D & 0x80000000) != 0; + Rm = (R & 0x80000000) != 0; + break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.flgAdd() invalid size'); + } + regs.v = (Sm && Dm && !Rm) || (!Sm && !Dm && Rm); + regs.c = (Sm && Dm) || (!Rm && Dm) || (Sm && !Rm); + regs.x = regs.c; + regs.n = Rm; + if (isADDX) { + if (R != 0) + regs.z = false; + } else + regs.z = R == 0; + } + + /*switch (z) { + case 1: if (S > 0xff || D > 0xff || R > 0xff) alert('fsub 8'); break; + case 2: if (S > 0xffff || D > 0xffff || R > 0xffff) alert('fsub 16'); break; + }*/ + function flgSub(S, D, R, z, isSUBX) /* SUB, SUBI, SUBQ, SUBX */ + { + var Sm, Dm, Rm; + + switch (z) { + case 1: + Sm = (S & 0x80) != 0; + Dm = (D & 0x80) != 0; + Rm = (R & 0x80) != 0; + break; + case 2: + Sm = (S & 0x8000) != 0; + Dm = (D & 0x8000) != 0; + Rm = (R & 0x8000) != 0; + break; + case 4: + Sm = (S & 0x80000000) != 0; + Dm = (D & 0x80000000) != 0; + Rm = (R & 0x80000000) != 0; + break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.flgSub() invalid size'); + } + regs.v = (!Sm && Dm && !Rm) || (Sm && !Dm && Rm); + regs.c = (Sm && !Dm) || (Rm && !Dm) || (Sm && Rm); + regs.x = regs.c; + regs.n = Rm; + if (isSUBX) { + if (R != 0) + regs.z = false; + } else + regs.z = R == 0; + } + + /*switch (z) { + case 1: if (S > 0xff || D > 0xff || R > 0xff) alert('fcmp 8'); break; + case 2: if (S > 0xffff || D > 0xffff || R > 0xffff) alert('fcmp 16'); break; + }*/ + function flgCmp(S, D, R, z) /* CMP, CMPA, CMPI, CMPM */ + { + var Sm, Dm, Rm; + + switch (z) { + case 1: + Sm = (S & 0x80) != 0; + Dm = (D & 0x80) != 0; + Rm = (R & 0x80) != 0; + break; + case 2: + Sm = (S & 0x8000) != 0; + Dm = (D & 0x8000) != 0; + Rm = (R & 0x8000) != 0; + break; + case 4: + Sm = (S & 0x80000000) != 0; + Dm = (D & 0x80000000) != 0; + Rm = (R & 0x80000000) != 0; + break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.flgCmp() invalid size'); + } + regs.v = (!Sm && Dm && !Rm) || (Sm && !Dm && Rm); + regs.c = (Sm && !Dm) || (Rm && !Dm) || (Sm && Rm); + regs.n = Rm; + regs.z = R == 0; + } + + /*switch (z) { + case 1: if (D > 0xff || R > 0xff) alert('fneg 8'); break; + case 2: if (D > 0xffff || R > 0xffff) alert('fneg 16'); break; + }*/ + function flgNeg(D, R, z, isNEGX) /* NEG, NEGX */ + { + var Dm, Rm; + + switch (z) { + case 1: + Dm = (D & 0x80) != 0; + Rm = (R & 0x80) != 0; + break; + case 2: + Dm = (D & 0x8000) != 0; + Rm = (R & 0x8000) != 0; + break; + case 4: + Dm = (D & 0x80000000) != 0; + Rm = (R & 0x80000000) != 0; + break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.flgNeg() invalid size'); + } + regs.v = Dm && Rm; + regs.c = Dm || Rm; + regs.x = regs.c; + regs.n = Rm; + if (isNEGX) { + if (R != 0) + regs.z = false; + } else + regs.z = R == 0; + } + + /*switch (z) { + case 1: if (R > 0xff) alert('flog 8'); break; + case 2: if (R > 0xffff) alert('flog 16'); break; + }*/ + function flgLogical(R, z) { /* AND ANDI OR ORI EOR EORI MOVE MOVEQ EXT NOT TST */ + switch (z) { + case 1: + regs.n = (R & 0x80) != 0; + break; + case 2: + regs.n = (R & 0x8000) != 0; + break; + case 4: + regs.n = (R & 0x80000000) != 0; + break; + default: + Fatal(SAEE_CPU_Internal, 'cpu.flgLogical() invalid size'); + } + regs.z = R == 0; + regs.v = regs.c = false; + } + + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + /* Data Movement */ + + function I_EXG(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + stEA(sea, p.z, d); + stEA(dea, p.z, s); + //ccna + //BUG.say(sprintf('I_EXG.%s s $%08x <-> d $%08x', szChr(p.z), s, d)); + return p.cyc;//6; + } + + function I_LEA(p) { + var sea = exEA(p.s, p.z); + var dea = exEA(p.d, p.z); + stEA(dea, p.z, sea.a); + //ccna + //BUG.say(sprintf('I_LEA.%s sea $%08x', szChr(p.z), sea.a)); + /*switch (sea.m) { + case M_ria: return 4; + case M_rid: return 8; + case M_rii: return 12; + case M_pcid: return 8; + case M_pcii: return 12; + case M_absw: return 8; + case M_absl: return 12; + }*/ + return p.cyc; + } + + function I_PEA(p) { + var sea = exEA(p.s, p.z); + var dea = exEA(new effAddr(M_ripr, 7), p.z); + stEA(dea, p.z, sea.a); + //ccna + return p.cyc; + } + + function I_LINK(p) { + var sea = exEA(p.s, p.z); + var An = sea.a; + var dea = exEA(p.d, p.z); + var dp = ldEA(dea, p.z); if (p.z == 2) dp = extWord(dp); + + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, regs.a[An]); + regs.a[An] = regs.a[7]; + regs.a[7] = add32(regs.a[7], dp); + //ccna + return p.cyc;//16; + + /*debug + var newsp = add32(regs.a[7], dp); + BUG.say(sprintf('I_LINK.%s A%d, dp $%08x, oldsp $%08x, newsp $%08x', szChr(p.z), An, dp, regs.a[7], newsp)); + regs.a[7] = newsp;*/ + } + + function I_UNLK(p) { + var sea = exEA(p.s, p.z); + var An = sea.a; + regs.a[7] = regs.a[An]; + regs.a[An] = ldEA(exEA(new effAddr(M_ripo, 7), 4), 4); + //ccna + //BUG.say(sprintf('I_UNLK.%s A%d', szChr(p.z), An)); + return p.cyc; + } + + function I_MOVE(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + stEA(dea, p.z, s); + flgLogical(s, p.z); + //BUG.say(sprintf('I_MOVE.%s sm %d dm %d sa $%08x da $%08x r $%08x', szChr(p.z), p.s.m, p.d.m, sea.a, dea.a, s)); + return p.cyc; + } + + function I_MOVEA(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); if (p.z == 2) s = extWord(s); + var dea = exEA(p.d, 4); + stEA(dea, 4, s); + //ccna + //BUG.say(sprintf('I_MOVEA.%s s $%08x A%d', szChr(p.z), s, p.d.r)); + return p.cyc; + } + + function I_MOVEQ(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); s = extByte(s); + var dea = exEA(p.d, p.z); + stEA(dea, p.z, s); + flgLogical(s, p.z); + //BUG.say(sprintf('I_MOVEQ.%s s $%08x', szChr(p.z), s)); + return p.cyc; //4; + } + + function I_MOVEM_R2M(p) { + /* p. 4-128: The MC68000 and MC68010 write the initial register value (not decremented). */ + var i, rd = [], ra = []; + for (i = 0; i < 8; i++) { + rd[i] = regs.d[i]; + ra[i] = regs.a[i]; + } + var sea = exEAM(p.s); + var dea = exEAM(p.d); + var n = 0, k; + + if (p.d.m == M_ripr) { + var c = 0; + for (var i = 0; i < 16; i++) { + if (sea.a & (1 << i)) c++; + } + c *= p.z; + regs.a[p.d.r] -= c; + dea.a -= c; + k = 15; + //BUG.say(sprintf('I_MOVEM_R2M.%s M_ripr bc %d == %d bytes', szChr(p.z), bc, bc * p.z)); + } else k = 0; + + for (var i = 0; i < 16; i++) { + if (sea.a & (1 << (i ^ k))) { + var r; + + if (i < 8) { + r = rd[i]; + //BUG.say(sprintf('I_MOVEM_R2M.%s D%d d $%08x', szChr(p.z), i, r)); + } else { + r = ra[i - 8]; + //BUG.say(sprintf('I_MOVEM_R2M.%s A%d d $%08x', szChr(p.z), i - 8, r)); + //if (i - 8 == p.d.r) BUG.say(sprintf('I_MOVEM_R2M.%s A%d d $%08x, WRITE OWN', szChr(p.z), i - 8, r)); + } + if (p.z == 2) + r &= 0xffff; + + stEA(dea, p.z, r); + dea.a += p.z; + n++; + } + } + //ccna + //BUG.say(sprintf('I_MOVEM_R2M.%s s $%08x d $%08x', szChr(p.z), sea.a, dea.a)); + return p.cyc + (p.z == 2 ? 4 : 8) * n; + } + + function I_MOVEM_M2R(p) { + var sea = exEAM(p.s); + var dea = exEAM(p.d); + var n = 0; + + for (var i = 0; i < 16; i++) { + if (sea.a & (1 << i)) { + var r = ldEA(dea, p.z); if (p.z == 2) r = extWord(r); + dea.a += p.z; + + if (i < 8) { + regs.d[i] = r; + //BUG.say(sprintf('I_MOVEM_M2R.%s D%d d $%08x', szChr(p.z), i, regs.d[i])); + } else { + regs.a[i - 8] = r; + //BUG.say(sprintf('I_MOVEM_M2R.%s A%d d $%08x', szChr(p.z), i - 8, regs.a[i - 8])); + } + n++; + } + } + if (p.d.m == M_ripo) { + //BUG.say(sprintf('I_MOVEM_M2R.%s RIPO old $%08x', szChr(p.z), regs.a[p.d.r])); + regs.a[p.d.r] = dea.a; + //BUG.say(sprintf('I_MOVEM_M2R.%s RIPO new $%08x', szChr(p.z), regs.a[p.d.r])); + } + //ccna + //BUG.say(sprintf('I_MOVEM_M2R.%s s $%08x d $%08x', szChr(p.z), sea.a, dea.a)); + return p.cyc + (p.z == 2 ? 4 : 8) * n; + } + + function I_MOVEP(p) { + var sea = exEA(p.s, p.z); + var dea = exEA(p.d, p.z); + + //M2R + if (sea.m == M_rid) { + var r; + + if (p.z == 2) { + r = ldEA(sea, 1) << 8; + sea.a += 2; + r += ldEA(sea, 1); + } else { + r = ldEA(sea, 1) << 24; + sea.a += 2; + r += ldEA(sea, 1) << 16; + sea.a += 2; + r += ldEA(sea, 1) << 8; + sea.a += 2; + r += ldEA(sea, 1); + r >>>= 0; + } + BUG.say(sprintf('I_MOVEP_M2R.%s A%d addr $%08x r $%08x', szChr(p.z), dea.a, sea.a - (p.z == 2 ? 4 : 8), r)); + stEA(dea, p.z, r); + } + //R2M + else { + var r = ldEA(sea, p.z); + + if (p.z == 2) { + stEA(dea, 1, r >> 8); + dea.a += 2; + stEA(dea, 1, r); + } else { + stEA(dea, 1, r >> 24); + dea.a += 2; + stEA(dea, 1, r >> 16); + dea.a += 2; + stEA(dea, 1, r >> 8); + dea.a += 2; + stEA(dea, 1, r); + } + BUG.say(sprintf('I_MOVEP_R2M.%s A%d addr $%08x r $%08x', szChr(p.z), sea.a, dea.a - (p.z == 2 ? 4 : 8), r)); + } + //ccna + return p.cyc; //p.z == 2 ? 16 : 24; + } + + /*-----------------------------------------------------------------------*/ + /* Integer Arithmetic */ + + function I_ADD(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = addAuto(s, d, p.z); + stEA(dea, p.z, r); + flgAdd(s, d, r, p.z, false); + //BUG.say(sprintf('I_ADD.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_ADDA(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); if (p.z == 2) s = extWord(s); + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + var r = add32(s, d); + stEA(dea, 4, r); + //ccna + //BUG.say(sprintf('I_ADDA.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_ADDI(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = addAuto(s, d, p.z); + stEA(dea, p.z, r); + flgAdd(s, d, r, p.z, false); + //BUG.say(sprintf('I_ADDI.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 16 : 8) : (p.z == 4 ? 20 : 12) + dea.c; + } + + function I_ADDQ(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + if (p.d.m == M_rda) { + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + var r = add32(s, d); + stEA(dea, 4, r); + //ccna + //return 8; + } else { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = addAuto(s, d, p.z); + stEA(dea, p.z, r); + flgAdd(s, d, r, p.z, false); + //return dea.m == M_rdd ? (p.z == 4 ? 8 : 4) : (p.z == 4 ? 12 : 8) + dea.c; + } + //BUG.say(sprintf('I_ADDQ.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_ADDX(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = addAuto(s, d, p.z); if (regs.x) r = addAuto(r, 1, p.z); + //var _x = regs.x?1:0; + stEA(dea, p.z, r); + flgAdd(s, d, r, p.z, true); + //BUG.say(sprintf('I_ADDX.%s s $%08x d $%08x xo %d xn %d r $%08x', szChr(p.z), s, d, _x, regs.x?1:0, r)); + return p.cyc;//dea.m == M_rdd ? (p.z == 4 ? 8 : 4) : (p.z == 4 ? 30 : 18); + } + + function I_CLR(p) { + var dea = exEA(p.d, p.z); + var foo = ldEA(dea, p.z); /* In the MC68000 and MC68008 a memory location is read before it is cleared. */ + stEA(dea, p.z, 0); + + regs.n = false; + regs.z = true; + regs.v = false; + regs.c = false; + //BUG.say(sprintf('I_CLR.%s', szChr(p.z))); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 6 : 4) : (p.z == 4 ? 12 : 8) + dea.c; + } + + function I_CMP(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); + flgCmp(s, d, r, p.z); + //BUG.say(sprintf('I_CMP.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_CMPA(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); if (p.z == 2) s = extWord(s); + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + var r = sub32(d, s); + flgCmp(s, d, r, 4); + //BUG.say(sprintf('I_CMPA.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_CMPI(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); + flgCmp(s, d, r, p.z); + //BUG.say(sprintf('I_CMPI.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 14 : 8) : (p.z == 4 ? 12 : 8) + dea.c; + } + + function I_CMPM(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); + flgCmp(s, d, r, p.z); + //BUG.say(sprintf('I_CMPM.%s s $%08x d $%08x r $%08x | %c', szChr(p.z), s, d, r, s)); + return p.cyc;//p.z == 4 ? 20 : 12; + } + + function I_DIVS(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); s = castWord(s); + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); d = castLong(d); + + regs.c = false; + if (s == 0) { + BUG.say(sprintf('I_DIVS NULL $%08x / $%08x', d, s)); + regs.pc = fault.pc; + return exception(5); + } else { + var quo = Math.floor(d / s); + + if (quo < 0) quo += 0x10000; + + if (quo < 0 || quo > 0xffff) { + regs.v = true; + //BUG.say(sprintf('I_DIVS.%s $%08x / $%08x = OVERFLOW (quo $%08x | rem $%08x)', szChr(p.z), d, s, quo, rem)); + } else { + var rem = d % s; + + if (rem && ((rem < 0) != (d < 0))) rem = -rem; + if (rem < 0) rem += 0x10000; + + regs.v = false; + regs.z = quo == 0; + regs.n = (quo & 0x8000) != 0; + + var r = ((rem << 16) | quo) >>> 0; + stEA(dea, 4, r); + //BUG.say(sprintf('I_DIVS.%s $%08x / $%08x = $%08x (quo $%08x | rem $%08x)', szChr(p.z), d, s, r, quo, rem)); + } + return p.cyc;// 158 + dea.c; + } + } + + function I_DIVU(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + + regs.c = false; + if (s == 0) { + BUG.say(sprintf('I_DIVU NULL $%08x / $%08x', d, s)); + regs.pc = fault.pc; + return exception(5); + } else { + var quo = Math.floor(d / s); + + if (quo > 0xffff) { + regs.v = true; + //BUG.say(sprintf('I_DIVU.%s $%08x / $%08x = OVERFLOW (quo $%08x | rem $%08x)', szChr(p.z), d, s, quo, rem)); + } else { + var rem = d % s; + + if (rem && (!!(rem & 0x8000) != !!(d & 0x80000000))) { + //var oldrem = rem; + rem = -rem + 0x10000; + //BUG.say(sprintf('I_DIVU d $%08x oldrem $%08x rem $%08x', d, oldrem, rem)); + } + regs.v = false; + regs.z = quo == 0; + regs.n = (quo & 0x8000) != 0; + + var r = ((rem << 16) | quo) >>> 0; + stEA(dea, 4, r); + //BUG.say(sprintf('I_DIVU.%s $%08x / $%08x = $%08x (quo $%08x | rem $%08x)', szChr(p.z), d, s, r, quo, rem)); + } + return p.cyc; //140 + dea.c; + } + } + + function I_EXT(p) { + var z = p.z == 2 ? 1 : 2; + var dea = exEA(p.d, z); + var d = ldEA(dea, z); + var r = p.z == 2 ? extByteToWord(d) : extWord(d); + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_EXT.%s d $%08x r $%08x', szChr(p.z), d, r)); + return p.cyc; //4; + } + + function I_MULS(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); s = castWord(s); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); d = castWord(d); + var r = s * d; + if (r < 0) r += 0x100000000; + stEA(dea, 4, r); + + regs.v = false; /* not possible for 16x16 */ + regs.c = false; + regs.n = (r & 0x80000000) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_MULS.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //70 + dea.c; + } + + function I_MULU(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = s * d; + stEA(dea, 4, r); + + regs.v = false; /* not possible for 16x16 */ + regs.c = false; + regs.n = (r & 0x80000000) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_MULU.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //70 + dea.c; + } + + function I_NEG(p) { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(0, d, p.z); + stEA(dea, p.z, r); + flgNeg(d, r, p.z, false); + //BUG.say(sprintf('I_NEG.%s d $%08x r $%08x', szChr(p.z), d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 6 : 4) : (p.z == 4 ? 12 : 8) + dea.c; + } + + function I_NEGX(p) { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(0, d, p.z); if (regs.x) r = subAuto(r, 1, p.z); + stEA(dea, p.z, r); + flgNeg(d, r, p.z, true); + //BUG.say(sprintf('I_NEGX.%s d $%08x x %d r $%08x', szChr(p.z), d, regs.x ? 1 : 0, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 6 : 4) : (p.z == 4 ? 12 : 8) + dea.c; + } + + function I_SUB(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); + stEA(dea, p.z, r); + flgSub(s, d, r, p.z, false); + //BUG.say(sprintf('I_SUB.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_SUBA(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); if (p.z == 2) s = extWord(s); + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + var r = sub32(d, s); + stEA(dea, 4, r); + //ccna + //BUG.say(sprintf('I_SUBA.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_SUBI(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); + stEA(dea, p.z, r); + flgSub(s, d, r, p.z, false); + //BUG.say(sprintf('I_SUBI.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 16 : 8) : (p.z == 4 ? 20 : 12) + dea.c; + } + + function I_SUBQ(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + if (p.d.m == M_rda) { + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + var r = sub32(d, s); + stEA(dea, 4, r); + //ccna + //return 8; + } else { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); + stEA(dea, p.z, r); + flgSub(s, d, r, p.z, false); + //return dea.m == M_rdd ? (p.z == 4 ? 8 : 4) : (p.z == 4 ? 12 : 8) + dea.c; + } + //BUG.say(sprintf('I_SUBQ.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_SUBX(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = subAuto(d, s, p.z); if (regs.x) r = subAuto(r, 1, p.z); + //var _x = regs.x?1:0; + stEA(dea, p.z, r); + flgSub(s, d, r, p.z, true); + //BUG.say(sprintf('I_SUBX.%s s $%08x d $%08x xo %d xn %d r $%08x', szChr(p.z), s, d, _x, regs.x?1:0, r)); + return p.cyc;//dea.m == M_rdd ? (p.z == 4 ? 8 : 4) : (p.z == 4 ? 30 : 18); + } + + /*-----------------------------------------------------------------------*/ + /* Logical */ + + function I_AND(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = (s & d) >>> 0; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_AND.%s s $%08x d $%08x r $%08x, cyc %d', szChr(p.z), s, d, r, p.cyc)); + return p.cyc; + } + + function I_ANDI(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = (s & d) >>> 0; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_ANDI.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 16 : 8) : (p.z == 4 ? 20 : 12) + dea.c; + } + + function I_EOR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = (s ^ d) >>> 0; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_EOR.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_EORI(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = (s ^ d) >>> 0; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_EORI.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 16 : 8) : (p.z == 4 ? 20 : 12) + dea.c; + } + + function I_NOT(p) { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var m = p.z == 1 ? 0xff : (p.z == 2 ? 0xffff : 0xffffffff); + var r = ~d & m; if (r < 0) r += 0x100000000; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_NOT.%s d $%08x r $%08x', szChr(p.z), d, r)); + return p.cyc;//dea.m == M_rdd ? (p.z == 4 ? 6 : 4) : (p.z == 4 ? 12 : 8) + dea.c; + } + + function I_OR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = (s | d) >>> 0; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_OR.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_ORI(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = (s | d) >>> 0; + stEA(dea, p.z, r); + flgLogical(r, p.z); + //BUG.say(sprintf('I_ORI.%s s $%08x d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc; //dea.m == M_rdd ? (p.z == 4 ? 16 : 8) : (p.z == 4 ? 20 : 12) + dea.c; + } + + /*-----------------------------------------------------------------------*/ + /* Shift and Rotate */ + + function I_ASL(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + var v = false; + var rm = r & p.ms; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & p.ms) != 0; + r <<= 1; + r = (r & p.mz) >>> 0; + if (!v && (r & p.ms) != rm) v = true; + } + stEA(dea, p.z, r); + regs.c = regs.x = c; + } else regs.c = false; + + regs.v = v; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_ASL.%s num %d d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_ASR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + var sign = (r & p.ms) ? p.ms : 0; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & 1) != 0; + r = (sign | (r >>> 1)) >>> 0; + } + stEA(dea, p.z, r); + regs.c = regs.x = c; + } else regs.c = false; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_ASR.%s num %d d $%08x r $%08x sign %d', szChr(p.z), s, d, r, sign)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_LSL(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & p.ms) != 0; + r <<= 1; + r = (r & p.mz) >>> 0; + } + stEA(dea, p.z, r); + regs.c = regs.x = c; + } else regs.c = false; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_LSL.%s num %d d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_LSR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & 1) != 0; + r >>>= 1; + } + stEA(dea, p.z, r); + regs.c = regs.x = c; + } else regs.c = false; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_LSR.%s num %d d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_ROL(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & p.ms) != 0; + r <<= 1; + r = (r & p.mz) >>> 0; + if (c) r = (r | 1) >>> 0; + } + stEA(dea, p.z, r); + regs.c = c; + } else regs.c = false; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_ROL.%s num %d d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_ROR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & 1) != 0; + r >>>= 1; + if (c) r = (p.ms | r) >>> 0; + } + stEA(dea, p.z, r); + regs.c = c; + } else regs.c = false; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_ROR.%s num %d d $%08x r $%08x', szChr(p.z), s, d, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_ROXL(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + var x = regs.x; //var _x = x?1:0; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & p.ms) != 0; + r <<= 1; + r = (r & p.mz) >>> 0; + if (x) r = (r | 1) >>> 0; + x = c; + } + stEA(dea, p.z, r); + regs.c = regs.x = c; + } else regs.c = regs.x; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_ROXL.%s num %d d $%08x ox %d nx %d r $%08x', szChr(p.z), s, d, _x, regs.x?1:0, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_ROXR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z) % 64; + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var n = s; + var r = d; + var c = false; + var x = regs.x; //var _x = x?1:0; + + if (n > 0) { + for (; n > 0; --n) { + c = (r & 1) != 0; + r >>>= 1; + if (x) r = (p.ms | r) >>> 0; + x = c; + } + stEA(dea, p.z, r); + regs.c = regs.x = c; + } else regs.c = regs.x; + + regs.v = false; + regs.n = (r & p.ms) != 0; + regs.z = r == 0; + //BUG.say(sprintf('I_ROXR.%s num %d d $%08x ox %d nx %d r $%08x', szChr(p.z), s, d, _x, regs.x?1:0, r)); + return p.cyc + (dea.m == M_rdd ? s << 1 : 0);//dea.m == M_rdd ? (p.z == 4 ? 8+n2 : 6+n2) : 8 + dea.c; + } + + function I_SWAP(p) { + var dea = exEA(p.d, 4); + var d = ldEA(dea, 4); + var r = ((d << 16) | (d >>> 16)) >>> 0; + stEA(dea, 4, r); + + regs.n = (r & 0x80000000) != 0; + regs.z = r == 0; + regs.v = false; + regs.c = false; + //BUG.say(sprintf('I_SWAP.%s d $%08x r $%08x', szChr(p.z), d, r)); + return p.cyc; //4; + } + + /*-----------------------------------------------------------------------*/ + /* Bit Manipulation */ + + function I_BCHG(p) { + var dz = p.d.m == M_rdd ? 4 : 1; + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, dz); + var d = ldEA(dea, dz); + var m = (1 << (s % (p.d.m == M_rdd ? 32 : 8))) >>> 0; + + var r = ((d & m) ? (d & ~m) : (d | m)) >>> 0; + stEA(dea, dz, r); + regs.z = (d & m) == 0; + + /*if (p.d.m == M_rdd) + BUG.say(sprintf('I_BCHG.%s s $%08x == m $%08x, d $%08x, r $%08x', szChr(p.z), s, m, d, r)); + else + BUG.say(sprintf('I_BCHG.%s s $%02x == m $%02x, d $%02x, r $%02x', szChr(p.z), s, m, d, r));*/ + + return p.cyc;//dea.m == M_rdd ? 8 : 8 + dea.c; + } + + function I_BCLR(p) { + var dz = p.d.m == M_rdd ? 4 : 1; + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, dz); + var d = ldEA(dea, dz); + var m = (1 << (s % (p.d.m == M_rdd ? 32 : 8))) >>> 0; + + var r = (d & ~m) >>> 0; + stEA(dea, dz, r); + regs.z = (d & m) == 0; + + /*if (p.d.m == M_rdd) + BUG.say(sprintf('I_BCLR.%s s $%08x == m $%08x, d $%08x, r $%08x', szChr(p.z), s, m, d, r)); + else + BUG.say(sprintf('I_BCLR.%s s $%02x == m $%02x, d $%02x, r $%02x', szChr(p.z), s, m, d, r));*/ + return p.cyc;//dea.m == M_rdd ? 10 : 8 + dea.c; + } + + function I_BSET(p) { + var dz = p.d.m == M_rdd ? 4 : 1; + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, dz); + var d = ldEA(dea, dz); + var m = (1 << (s % (p.d.m == M_rdd ? 32 : 8))) >>> 0; + + var r = (d | m) >>> 0; + stEA(dea, dz, r); + regs.z = (d & m) == 0; + + /*if (p.d.m == M_rdd) + BUG.say(sprintf('I_BSET.%s s $%08x == m $%08x, d $%08x, r $%08x', szChr(p.z), s, m, d, r)); + else + BUG.say(sprintf('I_BSET.%s s $%02x == m $%02x, d $%02x, r $%02x', szChr(p.z), s, m, d, r));*/ + return p.cyc;//dea.m == M_rdd ? 8 : 8 + dea.c; + } + + function I_BTST(p) { + var dz = p.d.m == M_rdd ? 4 : 1; + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, dz); + var d = ldEA(dea, dz); + var m = (1 << (s % (p.d.m == M_rdd ? 32 : 8))) >>> 0; + + regs.z = (d & m) == 0; + + /*if (p.d.m == M_rdd) + BUG.say(sprintf('I_BTST.%s s $%08x == m $%08x, d $%08x, r $%08x', szChr(p.z), s, m, d, r)); + else + BUG.say(sprintf('I_BTST.%s s $%02x == m $%02x, d $%02x, r $%02x', szChr(p.z), s, m, d, r));*/ + return p.cyc;//dea.m == M_rdd ? 6 : 4 + dea.c; + } + + /*-----------------------------------------------------------------------*/ + /* Binary-Coded Decimal */ + + function I_ABCD(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var x = regs.x ? 1 : 0; + var c = false; + + var s_h = (s >> 4) & 0xf; + var s_l = s & 0xf; + var d_h = (d >> 4) & 0xf; + var d_l = d & 0xf; + + var l = s_l + d_l + x; + if (l > 9) { + l -= 10; + c = true; + } + var h = s_h + d_h + (c ? 1 : 0); + c = false; + if (h > 9) { + h -= 10; + c = true; + } + var r = (h << 4) | l; + + stEA(dea, p.z, r); + + regs.x = regs.c = c; + if (r) regs.z = false; + if (undef) { + regs.n = !regs.n; //undef + regs.v = !regs.v; //undef + } + //BUG.say(sprintf('I_ABCD.%s s $%02x d $%02x x %d | s_h %d s_l %d d_h %d d_l %d | r $%02x c %d', szChr(p.z), s, d, x, s_h, s_l, d_h, d_l, r, c?1:0)); + return p.cyc; //dea.m == M_rdd ? 6 : 18; + } + + function I_NBCD(p) { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var c = false; + + var d_h = (d >> 4) & 0xf; + var d_l = d & 0xf; + + var l = 0 - d_l; + if (l < 0) { + l += 10; + c = true; + } + var h = 0 - d_h - (c ? 1 : 0); + c = false; + if (h < 0) { + h += 10; + c = true; + } + var r = (h << 4) | l; + + stEA(dea, p.z, r); + + regs.x = regs.c = c; + if (r) regs.z = false; + if (undef) { + regs.n = !regs.n; //undef + regs.v = !regs.v; //undef + } + //BUG.say(sprintf('I_NBCD.%s s $%02x d $%02x x %d | s_h %d s_l %d d_h %d d_l %d | r $%02x c %d', szChr(p.z), s, d, x, s_h, s_l, d_h, d_l, r, c?1:0)); + return p.cyc; //dea.m == M_rdd ? 6 : 8 + dea.c; + } + + function I_SBCD(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var x = regs.x ? 1 : 0; + var c = false; + + var s_h = (s >> 4) & 0xf; + var s_l = s & 0xf; + var d_h = (d >> 4) & 0xf; + var d_l = d & 0xf; + + var l = d_l - s_l - x; + if (l < 0) { + l += 10; + c = true; + } + var h = d_h - s_h - (c ? 1 : 0); + c = false; + if (h < 0) { + h += 10; + c = true; + } + var r = (h << 4) | l; + + stEA(dea, p.z, r); + + regs.x = regs.c = c; + if (r) regs.z = false; + if (undef) { + regs.n = !regs.n; //undef + regs.v = !regs.v; //undef + } + //BUG.say(sprintf('I_SBCD.%s s $%02x d $%02x x %d | s_h %d s_l %d d_h %d d_l %d | r $%02x c %d', szChr(p.z), s, d, x, s_h, s_l, d_h, d_l, r, c?1:0)); + return p.cyc; //dea.m == M_rdd ? 6 : 18; + } + + /*-----------------------------------------------------------------------*/ + /* Program Control */ + + function I_Bcc(p) { + var cc = p.c.cc; + var dp = p.c.dp; + var dp16; + var pc; + + if (dp == 0) dp16 = nextIWord(); + //else if (dp == 255) Fatal(SAEE_CPU_68020_Required, 'cpu.I_Bcc() Full extension index detected (not a 68000 programm)'); + + if (ccTrue(cc)) { + if (dp == 0) pc = add32(regs.pc - 2, extWord(dp16)); + else pc = add32(regs.pc, extByte(dp)); + //BUG.say(sprintf('I_Bcc pc $%08x', pc)); + setPC(pc); + return p.cycTaken; //10; + } + //ccna + return p.cyc;//dp == 0 ? 12 : 8; + } + + function I_DBcc(p) { + var cc = p.c.cc; + var dp = nextIWord(); + var cyc; + + if (!ccTrue(cc)) { + var ea = exEA(new effAddr(M_rdd, p.c.dr), p.z); + var dr = ldEA(ea, p.z); + + if (dr--) { + var pc = add32(regs.pc - 2, extWord(dp)); + setPC(pc); + cyc = p.cycFalseTaken; //10; + } else { + dr = 0xffff; + cyc = p.cycFalse; //14; + } + stEA(ea, p.z, dr); + } else cyc = p.cycTrue; //12; + //ccna + return cyc; + } + + function I_Scc(p) { + //var cc = p.s.r; + var cc = p.c.cc; + var dea = exEA(p.d, p.z); + var foo = ldEA(dea, p.z); /* In the MC68000 and MC68008 a memory location is read before it is cleared. */ + var isTrue = ccTrue(cc); + stEA(dea, p.z, isTrue ? 0xff : 0); + //ccna + //BUG.say(sprintf('I_S%s, cc %d, ccTrue %d, cyc %d', ccNames[cc], cc, ccTrue(cc)?1:0, isTrue ? p.cycTrue : p.cycFalse)); + return isTrue ? p.cycTrue : p.cycFalse; //dea.m == M_rdd ? (isTrue ? 6 : 4) : 8 + dea.c; + } + + function I_BRA(p) { + var dp = p.c.dp; + var pc; + + if (dp == 0) { + dp = extWord(nextIWord()); + pc = add32(regs.pc - 2, dp); + } + //else if (dp == 255) Fatal(SAEE_CPU_68020_Required, 'cpu.I_BRA() Full extension index detected (not a 68000 programm)'); + else pc = add32(regs.pc, extByte(dp)); + + setPC(pc); + //ccna + return p.cycTaken; //10; + } + + function I_BSR(p) { + var dp = p.c.dp; + var pc; + + if (dp == 0) { + dp = extWord(nextIWord()); + pc = add32(regs.pc - 2, dp); + } + //else if (dp == 255) Fatal(SAEE_CPU_68020_Required, 'cpu.I_BSR() Full extension index detected (not a 68000 programm)'); + else pc = add32(regs.pc, extByte(dp)); + + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, regs.pc); + setPC(pc); + //ccna + return p.cycTaken; //18; + } + + function I_JMP(p) { + var dea = exEA(p.d, p.z); + setPC(dea.a); + //ccna + //BUG.say(sprintf('I_JMP $%08x', dea.a)); + /*switch (dea.m) { + case M_ria: return 8; + case M_rid: return 10; + case M_rii: return 14; + case M_pcid: return 10; + case M_pcii: return 14; + case M_absw: return 10; + case M_absl: return 12; + }*/ + return p.cyc; + } + + function I_JSR(p) { + var dea = exEA(p.d, p.z); + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, regs.pc); + setPC(dea.a); + //ccna + //BUG.say(sprintf('I_JSR $%08x', dea.a)); + /*switch (dea.m) { + case M_ria: return 16; + case M_rid: return 18; + case M_rii: return 22; + case M_pcid: return 18; + case M_pcii: return 22; + case M_absw: return 18; + case M_absl: return 20; + }*/ + return p.cyc; + } + + function I_RTR(p) { + var ccr = ldEA(exEA(new effAddr(M_ripo, 7), 2), 2) & 0xff; + var pc = ldEA(exEA(new effAddr(M_ripo, 7), 4), 4); + setCCR(ccr); + setPC(pc); + //BUG.say(sprintf('I_RTR crr $%04x pc $%08x', crr, pc)); + return p.cyc; + } + + function I_RTS(p) { + var pc = ldEA(exEA(new effAddr(M_ripo, 7), 4), 4); + //BUG.say(sprintf('I_RTS() regs.pc $%08x newpc $%08x', regs.pc, pc)); + setPC(pc); + //ccna + return p.cyc; + } + + function I_TST(p) { + var dea = exEA(p.d, p.z); + var r = ldEA(dea, p.z); //r = extAuto(r, p.z); + flgLogical(r, p.z); + //BUG.say(sprintf('I_TST.%s r $%08x', szChr(p.z), r)); + return p.cyc; //dea.m == M_rdd || dea.m == M_rda ? 4 : 4 + dea.c; + } + + function I_NOP(p) { + //BUG.say('I_NOP'); + return p.cyc; + } + + /*-----------------------------------------------------------------------*/ + /* System Control - CCR */ + + function I_ANDI_CCR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var d = getCCR(); + var r = s & d; + setCCR(r); + //BUG.say(sprintf('I_ANDI_CCR.%s val $%02x, old $%02x new $%02x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_EORI_CCR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var d = getCCR(); + var r = s ^ d; + setCCR(r); + //BUG.say(sprintf('I_EORI_CCR.%s val $%02x, old $%02x new $%02x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_ORI_CCR(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var d = getCCR(); + var r = s | d; + setCCR(r); + //BUG.say(sprintf('I_ORI_CCR.%s val $%02x, old $%02x new $%02x', szChr(p.z), s, d, r)); + return p.cyc; + } + + function I_MOVE_2CCR(p) { + var sea = exEA(p.s, p.z); + var ccr = ldEA(sea, p.z) & 0xff; + //BUG.say(sprintf('I_MOVE_2CCR.%s old $%02x new $%02x', szChr(p.z), getCCR(), ccr)); + setCCR(ccr); + return p.cyc; + } + + /*function I_MOVE_CCR2(p) { //ups, not for the 68000 + var ccr = getCCR(); + var dea = exEA(p.d, p.z); + stEA(dea, p.z, ccr); + //ccna + //BUG.say(sprintf('I_MOVE_CCR2.%s $%02x', szChr(p.z), ccr)); + return p.cyc; + }*/ + + /*-----------------------------------------------------------------------*/ + /* System Control - SR */ + + function I_ANDI_SR(p) { + if (regs.s) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var d = getSR(); + var r = s & d; + //BUG.say(sprintf('I_ANDI_SR.%s val $%02x, old $%02x new $%02x', szChr(p.z), s, d, r)); + setSR(r); + return p.cyc; + } else { + //BUG.say('I_ANDI_SR PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_EORI_SR(p) { + if (regs.s) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var d = getSR(); + var r = s ^ d; + //BUG.say(sprintf('I_EORI_SR.%s val $%02x, old $%02x new $%02x', szChr(p.z), s, d, r)); + setSR(r); + return p.cyc; + } else { + //BUG.say('I_EORI_SR PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_ORI_SR(p) { + if (regs.s) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var d = getSR(); + var r = s | d; + //BUG.say(sprintf('I_ORI_SR.%s val $%02x, old $%02x new $%02x', szChr(p.z), s, d, r)); + setSR(r); + return p.cyc; + } else { + //BUG.say('I_ORI_SR PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_MOVE_2SR(p) { + if (regs.s) { + var sea = exEA(p.s, p.z); + var sr = ldEA(sea, p.z); + //BUG.say(sprintf('I_MOVE_2SR.%s sr $%04x', szChr(p.z), sr)); + setSR(sr); + return p.cyc; + } else { + //BUG.say('I_MOVE_2SR PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_MOVE_SR2(p) { + var sr = getSR(); + var dea = exEA(p.d, p.z); + var foo = ldEA(dea, p.z); /* Memory destination is read before it is written to. */ + stEA(dea, p.z, sr); + //ccna + //BUG.say(sprintf('I_MOVE_SR2.%s sr $%04x', szChr(p.z), sr)); + return p.cyc;//dea.m == M_rdd ? 6 : 8; + } + + /*-----------------------------------------------------------------------*/ + /* System Control - USP */ + + function I_MOVE_USP2A(p) { + if (regs.s) { + var dea = exEA(p.d, p.z); + stEA(dea, p.z, regs.usp); + return p.cyc; + } else { + //BUG.say('I_MOVE_USP PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_MOVE_A2USP(p) { + if (regs.s) { + var sea = exEA(p.s, p.z); + regs.usp = ldEA(sea, p.z); + return p.cyc; + } else { + //BUG.say('I_MOVE_USP PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + /*-----------------------------------------------------------------------*/ + /* System Control */ + + function I_CHK(p) { + var sea = exEA(p.s, p.z); + var s = ldEA(sea, p.z); + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + + //BUG.say(sprintf('I_CHK.%s s $%08x d $%08x (d>s||d<0)', szChr(p.z), s, d)); + + if (undef) { + regs.z = !regs.z; //undef + regs.v = !regs.v; //undef + regs.c = !regs.c; //undef + } + if (d > s) { + regs.n = false; + regs.pc = fault.pc; + return exception(6) + p.cycTaken; + } else if (d & 0x8000) { /* 68000 word only */ + regs.n = true; + regs.pc = fault.pc; + return exception(6) + p.cycTaken; + } + return p.cyc; + } + + function I_ILLEGAL(p) { + var op = fault.op; + var pc = fault.pc; + + if (op == 0x4E7B && AMIGA.mem.load32(0x10) == 0 && (pc & 0xf80000) == 0xf80000) + Fatal(SAEE_CPU_68020_Required, 'Your Kickstart requires a 68020'); + + if ((op & 0xf000) == 0xf000) { + BUG.say(sprintf('I_ILLEGAL exception 11, line F[1111] emulator, op $%04x, pc $%08x', op, pc)); + //AMIGA.cpu.diss(fault.pc - 8, 20); + //AMIGA.cpu.dump(); + regs.pc = fault.pc; + return exception(11); + } else if ((op & 0xf000) == 0xa000) { + BUG.say(sprintf('I_ILLEGAL exception 10, line A[1010] emulator, op $%04x, pc $%08x', op, pc)); + //AMIGA.cpu.diss(fault.pc - 8, 20); + //AMIGA.cpu.dump(); + regs.pc = fault.pc; + return exception(10); + } + + BUG.say(sprintf('I_ILLEGAL exception 4, op $%04x, pc $%08x', op, pc)); + //AMIGA.cpu.diss(fault.pc - 8, 20); + //AMIGA.cpu.dump(); + regs.pc = fault.pc; + return exception(4); + //ccna + } + + function I_RESET(p) { + if (regs.s) { + BUG.say('I_RESET()'); + AMIGA.reset(); + return p.cyc; + } else { + //BUG.say('I_RESET PRIVILIG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_RTE(p) { + if (regs.s) { + var sr = ldEA(exEA(new effAddr(M_ripo, 7), 2), 2); + var pc = ldEA(exEA(new effAddr(M_ripo, 7), 4), 4); + setSR(sr); + //BUG.say(sprintf('I_RTE sr $%04x newpc $%08x oldpc $%08x', sr, pc, regs.pc)); + setPC(pc); + return p.cyc; + } else { + //BUG.say('I_RTE PRIVILEG VIOLATION'); + regs.pc = fault.pc; + return exception(8); + } + } + + function I_STOP(p) { + if (regs.s) { + var sea = exEA(p.s, p.z); + var sr = ldEA(sea, p.z); + setSR(sr); + + regs.stopped = true; + if ((AMIGA.spcflags & SPCFLAG_DOTRACE) == 0) + set_special(SPCFLAG_STOP); + + //BUG.say(sprintf('I_STOP() new sr $%04x', regs.sr)); + return p.cyc; + } else { + regs.pc = fault.pc; + return exception(8); + } + } + + function I_TRAP(p) { + var dea = exEA(p.d, p.z); + var vec = ldEA(dea, p.z); + //BUG.say(sprintf('I_TRAP exception 32 + %d', vec)); + return exception(32 + vec); + //ccna + } + + function I_TRAPV(p) { + if (regs.v) { + BUG.say('I_TRAPV exception 7'); + return exception(7); + } + //ccna + return p.cyc; + } + + function I_TAS(p) { + var dea = exEA(p.d, p.z); + var d = ldEA(dea, p.z); + var r = 0x80 | d; + stEA(dea, p.z, r); + regs.n = (d & 0x80) != 0; + regs.z = d == 0; + regs.v = false; + regs.c = false; + BUG.say(sprintf('I_TAS.%s d $%02x r $%02x', szChr(p.z), d, r)); + return p.cyc; //dea.m == M_rdd ? 4 : 10 + dea.c; + } + + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + + function mkCyc(z, m) { + switch (m) { + case M_rdd: + case M_rda: return 0; + case M_ria: return z == 4 ? 8 : 4; + case M_ripo: return z == 4 ? 8 : 4; + case M_ripr: return z == 4 ? 10 : 6; + case M_rid: return z == 4 ? 12 : 8; + case M_rii: return z == 4 ? 14 : 10; + case M_pcid: return z == 4 ? 12 : 8; + case M_pcii: return z == 4 ? 14 : 10; + case M_absw: return z == 4 ? 12 : 8; + case M_absl: return z == 4 ? 16 : 12; + case M_imm: + case M_list: return z == 4 ? 8 : 4; + } + } + + function mkN(op, mn, cyc) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + if (cyc) i.p.cyc = cyc; + return i; + } + + function mkS(op, mn, z, s, r, cyc, add) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + i.p.z = z; + i.p.s = new effAddr(s, r); + i.p.s.c = mkCyc(z, s); + i.p.cyc = cyc; + if (add) i.p.cyc += i.p.s.c; + return i; + } + + function mkD(op, mn, z, d, r, cyc, add) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + i.p.z = z; + i.p.d = new effAddr(d, r); + i.p.d.c = mkCyc(z, d); + i.p.cyc = cyc; + if (add) i.p.cyc += i.p.d.c; + return i; + } + + function mkSD(op, mn, z, sm, sr, dm, dr, cyc, sa, da) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + i.p.z = z; + i.p.ms = z == 1 ? 0x80 : (z == 2 ? 0x8000 : 0x80000000); + i.p.mz = z == 1 ? 0xff : (z == 2 ? 0xffff : 0xffffffff); + i.p.s = new effAddr(sm, sr); + i.p.d = new effAddr(dm, dr); + i.p.s.c = mkCyc(z, sm); + i.p.d.c = mkCyc(z, dm); + i.p.cyc = cyc; + if (sa) i.p.cyc += i.p.s.c; + if (da) i.p.cyc += i.p.d.c; + return i; + } + + function mkC(op, mn, sz, cc, dp, dr, cycTaken, cyc) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + i.p.z = sz; + i.p.c = new iCon(cc, dp, dr); + if (cycTaken !== null) i.p.cycTaken = cycTaken; + if (cyc !== null) i.p.cyc = cyc; + return i; + } + + function mkDBcc(op, mn, sz, cc, dp, dr, cycTrue, cycFalseTaken, cycFalse) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + i.p.z = sz; + i.p.c = new iCon(cc, dp, dr); + i.p.cycTrue = cycTrue; + i.p.cycFalseTaken = cycFalseTaken; + i.p.cycFalse = cycFalse; + return i; + } + + function mkCD(op, mn, z, cc, dp, dr, m, r, cycTrue, cycFalse, add) { + var i = new iDef(); + i.op = op; + i.pr = false; + i.mn = mn; + i.f = null; + i.p = {}; + i.p.z = z; + i.p.c = new iCon(cc, dp, dr); + i.p.d = new effAddr(m, r); + i.p.d.c = mkCyc(z, m); + i.p.cycTrue = cycTrue; + i.p.cycFalse = cycFalse; + if (add) { + i.p.cycTrue += i.p.d.c; + i.p.cycFalse += i.p.d.c; + } + return i; + } + + function mkEA(mr, en, inv) { + var m = (mr >> 3) & 7; + var r = mr & 7; + var b = inv ? (r << 3) | m : (m << 3) | r; + + if (m != 7) { + switch (m) { + case 0: { if (en.indexOf(M_rdd) != -1) return [b, M_rdd, r]; break; } + case 1: { if (en.indexOf(M_rda) != -1) return [b, M_rda, r]; break; } + case 2: { if (en.indexOf(M_ria) != -1) return [b, M_ria, r]; break; } + case 3: { if (en.indexOf(M_ripo) != -1) return [b, M_ripo, r]; break; } + case 4: { if (en.indexOf(M_ripr) != -1) return [b, M_ripr, r]; break; } + case 5: { if (en.indexOf(M_rid) != -1) return [b, M_rid, r]; break; } + case 6: { if (en.indexOf(M_rii) != -1) return [b, M_rii, r]; break; } + } + } else { + if (r == 0 && en.indexOf(M_absw) != -1) return [b, M_absw, -1]; + if (r == 1 && en.indexOf(M_absl) != -1) return [b, M_absl, -1]; + if (r == 2 && en.indexOf(M_pcid) != -1) return [b, M_pcid, -1]; + if (r == 3 && en.indexOf(M_pcii) != -1) return [b, M_pcii, -1]; + if (r == 4 && en.indexOf(M_imm) != -1) return [b, M_imm, -1]; + } + return [-1, -1, -1]; + } + + /* Start of the fun part... */ + function mkiTab() { + var op, cnt = 0; + + iTab = new Array(0x10000); + for (op = 0; op < 0x10000; op++) { + iTab[op] = new iDef(); + iTab[op].op = -1; + iTab[op].pr = false; + iTab[op].mn = 'ILLEGAL'; + iTab[op].f = I_ILLEGAL; + iTab[op].p = null; + } + + //ABCD + { + var rm, Rx, Ry; + + for (rm = 0; rm < 2; rm++) { + for (Rx = 0; Rx < 8; Rx++) { + for (Ry = 0; Ry < 8; Ry++) { + op = (12 << 12) | (Rx << 9) | (1 << 8) | (rm << 3) | Ry; + + if (iTab[op].op === -1) { + if (rm == 0) + iTab[op] = mkSD(op, 'ABCD', 1, M_rdd, Ry, M_rdd, Rx, 6, false, false); + else + iTab[op] = mkSD(op, 'ABCD', 1, M_ripr, Ry, M_ripr, Rx, 18, false, false); + + iTab[op].f = I_ABCD; + cnt++; + } else { + BUG.say('OP EXISTS ABCD ' + op); + return false; + } + } + } + } + } + //ADD + { + var z, z2, dir, en, Dn, mr, ea, cyc; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (dir = 0; dir < 2; dir++) { + if (dir == 0) en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + else en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + if (dir == 0 && ea[1] == M_rda && z == 0) continue; //An word and long only + + op = (13 << 12) | (Dn << 9) | (dir << 8) | (z << 6) | ea[0]; + + cyc = dir == 0 ? (z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : 4) : (z2 == 4 ? 12 : 8); + + if (iTab[op].op === -1) { + if (dir == 0) + iTab[op] = mkSD(op, 'ADD', z2, ea[1], ea[2], M_rdd, Dn, cyc, true, false); + else + iTab[op] = mkSD(op, 'ADD', z2, M_rdd, Dn, ea[1], ea[2], cyc, false, true); + + iTab[op].f = I_ADD; + //iTab[op].p.cyc = ((dir == 0) ? (z2 == 4 ? (ea[1]==M_rdd||ea[1]==M_imm?8:6) : 4) : (z2 == 4 ? 12 : 8)) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS ADD ' + op); + return false; + } + } + } + } + } + } + } + //ADDA + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var z, z2, z3, An, mr, ea; + + for (z = 0; z < 2; z++) { + z2 = z == 0 ? 2 : 4; + z3 = z == 0 ? 3 : 7; + for (An = 0; An < 8; An++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (13 << 12) | (An << 9) | (z3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'ADDA', z2, ea[1], ea[2], M_rda, An, z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : 8, true, false); + iTab[op].f = I_ADDA; + //iTab[op].p.cyc = (z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : 8) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS ADDA ' + op); + return false; + } + } + } + } + } + } + //ADDI + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (6 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'ADDI', z2, M_imm, -1, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 16 : 8) : (z2 == 4 ? 20 : 12), false, ea[1] != M_rdd); + iTab[op].f = I_ADDI; + cnt++; + } else { + BUG.say('OP EXISTS ADDI ' + op); + return false; + } + } + } + } + } + //ADDQ + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, id, mr, ea, cyc; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (id = 0; id < 8; id++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + if (ea[1] == M_rda && z == 0) continue; //An word and long only + + op = (5 << 12) | (id << 9) | (z << 6) | ea[0]; + cyc = ea[1] == M_rda ? 8 : (ea[1] == M_rdd ? (z2 == 4 ? 8 : 4) : (z2 == 4 ? 12 : 8)); + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'ADDQ', z2, M_imm, id == 0 ? 8 : id, ea[1], ea[2], cyc, false, ea[1] != M_rdd && ea[1] != M_rda); + iTab[op].f = I_ADDQ; + cnt++; + } else { + BUG.say('OP EXISTS ADDQ ' + op); + return false; + } + } + } + } + } + } + //ADDX + { + var z, z2, rm, Rx, Ry; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (rm = 0; rm < 2; rm++) { + for (Rx = 0; Rx < 8; Rx++) { + for (Ry = 0; Ry < 8; Ry++) { + op = (13 << 12) | (Rx << 9) | (1 << 8) | (z << 6) | (rm << 3) | Ry; + + if (iTab[op].op === -1) { + if (rm == 0) + iTab[op] = mkSD(op, 'ADDX', z2, M_rdd, Ry, M_rdd, Rx, z2 == 4 ? 8 : 4, false, false); + else + iTab[op] = mkSD(op, 'ADDX', z2, M_ripr, Ry, M_ripr, Rx, z2 == 4 ? 30 : 18, false, false); + + iTab[op].f = I_ADDX; + cnt++; + } else { + BUG.say('OP EXISTS ADDX ' + op + ' ' + iTab[op].mn + ' ' + iTab[op].p.s.r + ' ' + iTab[op].p.d.r); + return false; + } + } + } + } + } + } + //AND + { + var z, z2, dir, en, Dn, mr, ea, cyc; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (dir = 0; dir < 2; dir++) { + if (dir == 0) en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + else en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (12 << 12) | (Dn << 9) | (dir << 8) | (z << 6) | ea[0]; + + cyc = dir == 0 ? (z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : 4) : (z2 == 4 ? 12 : 8); + + if (iTab[op].op === -1) { + if (dir == 0) + iTab[op] = mkSD(op, 'AND', z2, ea[1], ea[2], M_rdd, Dn, cyc, true, false); + else + iTab[op] = mkSD(op, 'AND', z2, M_rdd, Dn, ea[1], ea[2], cyc, false, true); + + iTab[op].f = I_AND; + //iTab[op].p.cyc = (dir == 0 ? (z2 == 4 ? (ea[1]==M_rdd||ea[1]==M_imm?8:6) : 4) : (z2 == 4 ? 12 : 8)) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS AND ' + op); + return false; + } + } + } + } + } + } + } + //ANDI + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (2 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'ANDI', z2, M_imm, -1, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 16 : 8) : (z2 == 4 ? 20 : 12), false, ea[1] != M_rdd); + iTab[op].f = I_ANDI; + cnt++; + } else { + BUG.say('OP EXISTS ANDI ' + op); + return false; + } + } + } + } + } + //ANDI_CCR + { + op = 0x23C; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'ANDI_CCR', 1, M_imm, -1, 20, false); + iTab[op].f = I_ANDI_CCR; + cnt++; + } else { + BUG.say('OP EXISTS ANDI ' + op); + return false; + } + } + //ANDI_SR + { + op = 0x27C; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'ANDI_SR', 2, M_imm, -1, 20, false); + iTab[op].pr = true; + iTab[op].f = I_ANDI_SR; + cnt++; + } else { + BUG.say('OP EXISTS ANDI ' + op); + return false; + } + } + //ASL,ASR + { + var en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, dr, ir, cr, Dy, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + + for (dr = 0; dr < 2; dr++) { + for (ir = 0; ir < 2; ir++) { + for (cr = 0; cr < 8; cr++) { + for (Dy = 0; Dy < 8; Dy++) { + op = (14 << 12) | (cr << 9) | (dr << 8) | (z << 6) | (ir << 5) | Dy; + + if (iTab[op].op === -1) { + if (ir == 0) + iTab[op] = mkSD(op, dr == 0 ? 'ASR_RI' : 'ASL_RI', z2, M_imm, cr == 0 ? 8 : cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + else + iTab[op] = mkSD(op, dr == 0 ? 'ASR_RD' : 'ASL_RD', z2, M_rdd, cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + + iTab[op].f = dr == 0 ? I_ASR : I_ASL; + cnt++; + } else { + BUG.say('OP EXISTS ASx ' + op); + return false; + } + } + } + } + } + } + for (dr = 0; dr < 2; dr++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (112 << 9) | (dr << 8) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, dr == 0 ? 'ASR_M' : 'ASL_M', 2, M_imm, 1, ea[1], ea[2], 8, false, true); + iTab[op].f = dr == 0 ? I_ASR : I_ASL; + cnt++; + } else { + BUG.say('OP EXISTS ASx ' + op); + return false; + } + } + } + } + } + //Bcc + { + var cc, dp; + + for (cc = 2; cc < 16; cc++) { + for (dp = 0; dp < 255; dp++) /* 0xff = 68020 only */ + { + op = (6 << 12) | (cc << 8) | dp; + + if (iTab[op].op === -1) { + iTab[op] = mkC(op, 'B' + ccNames[cc], dp == 0 ? 1 : 2, cc, dp, -1, 10, dp == 0 ? 12 : 8); + iTab[op].f = I_Bcc; + cnt++; + } else { + BUG.say('OP EXISTS B' + ccNames[cc] + ' ' + op); + return false; + } + } + } + } + //BCHG + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (Dn << 9) | (5 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BCHG1', 4, M_rdd, Dn, ea[1], ea[2], 8, false, false); + iTab[op].f = I_BCHG; + cnt++; + } else { + BUG.say('OP EXISTS BCHG1 ' + op); + return false; + } + } + } + } + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (33 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BCHG2', 1, M_imm, -1, ea[1], ea[2], 8, false, true); + iTab[op].f = I_BCHG; + cnt++; + } else { + BUG.say('OP EXISTS BCHG2 ' + op); + return false; + } + } + } + } + //BCLR + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (Dn << 9) | (6 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BCLR1', 4, M_rdd, Dn, ea[1], ea[2], 10, false, false); + iTab[op].f = I_BCLR; + cnt++; + } else { + BUG.say('OP EXISTS BCHG1 ' + op); + return false; + } + } + } + } + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (34 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BCLR2', 1, M_imm, - 1, ea[1], ea[2], 8, false, true); + iTab[op].f = I_BCLR; + cnt++; + } else { + BUG.say('OP EXISTS BCLR2 ' + op); + return false; + } + } + } + } + //BSET + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (Dn << 9) | (7 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BSET1', 4, M_rdd, Dn, ea[1], ea[2], 8, false, false); + iTab[op].f = I_BSET; + cnt++; + } else { + BUG.say('OP EXISTS BSET1 ' + op); + return false; + } + } + } + } + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (35 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BSET2', 1, M_imm, - 1, ea[1], ea[2], 8, false, true); + iTab[op].f = I_BSET; + cnt++; + } else { + BUG.say('OP EXISTS BSET2 ' + op); + return false; + } + } + } + } + //BTST + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (Dn << 9) | (4 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BTST1', 4, M_rdd, Dn, ea[1], ea[2], 6, false, false); + iTab[op].f = I_BTST; + cnt++; + } else { + BUG.say('OP EXISTS BTST1 ' + op); + return false; + } + } + } + } + en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl]; + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (32 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'BTST2', 1, M_imm, -1, ea[1], ea[2], 8, false, true); + iTab[op].f = I_BTST; + cnt++; + } else { + BUG.say('OP EXISTS BTST2 ' + op); + return false; + } + } + } + } + //BRA + { + var dp; + + for (dp = 0; dp < 255; dp++) /* 0xff = 68020 only */ + { + op = (96 << 8) | dp; + + if (iTab[op].op === -1) { + iTab[op] = mkC(op, 'BRA', dp == 0 ? 1 : 2, 0, dp, -1, 10, null); + iTab[op].f = I_BRA; + cnt++; + } else { + BUG.say('OP EXISTS BRA ' + op); + return false; + } + } + } + //BSR + { + var dp; + + for (dp = 0; dp < 255; dp++) /* 0xff = 68020 only */ + { + op = (97 << 8) | dp; + + if (iTab[op].op === -1) { + iTab[op] = mkC(op, 'BSR', dp == 0 ? 1 : 2, 1, dp, -1, 18, null); + iTab[op].f = I_BSR; + cnt++; + } else { + BUG.say('OP EXISTS BSR ' + op); + return false; + } + } + } + //CHK + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var z2 = 2, + z3 = 3, + Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (4 << 12) | (Dn << 9) | (z3 << 7) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'CHK', z2, ea[1], ea[2], M_rdd, Dn, 10, true, false); + iTab[op].f = I_CHK; + iTab[op].p.cycTaken = iTab[op].p.s.c; + cnt++; + } else { + BUG.say('OP EXISTS CHK ' + op); + return false; + } + } + } + } + } + //CLR + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (66 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'CLR', z2, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 6 : 4) : (z2 == 4 ? 12 : 8), ea[1] != M_rdd); + iTab[op].f = I_CLR; + cnt++; + } else { + BUG.say('OP EXISTS CLR ' + op); + return false; + } + } + } + } + } + //CMP + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var z, z2, Dn, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + if (ea[1] == M_rda && z == 0) continue; //An word and long only + + op = (11 << 12) | (Dn << 9) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'CMP', z2, ea[1], ea[2], M_rdd, Dn, z2 == 4 ? 6 : 4, true, false); + iTab[op].f = I_CMP; + //iTab[op].p.cyc = (z2 == 4 ? 6 : 4) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS CMP ' + op); + return false; + } + } + } + } + } + } + //CMPA + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var z, z2, z3, An, mr, ea; + + for (z = 1; z < 3; z++) { + z2 = z == 1 ? 2 : 4; + z3 = z == 1 ? 3 : 7; + for (An = 0; An < 8; An++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (11 << 12) | (An << 9) | (z3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'CMPA', z2, ea[1], ea[2], M_rda, An, 6, true, false); + iTab[op].f = I_CMPA; + //iTab[op].p.cyc = 6 + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS CMPA ' + op); + return false; + } + } + } + } + } + } + //CMPI + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (12 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'CMPI', z2, M_imm, -1, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 14 : 8) : (z2 == 4 ? 12 : 8), false, ea[1] != M_rdd); + iTab[op].f = I_CMPI; + cnt++; + } else { + BUG.say('OP EXISTS CMPI ' + op); + return false; + } + } + } + } + } + //CMPM + { + var z, z2, Ax, Ay; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (Ax = 0; Ax < 8; Ax++) { + for (Ay = 0; Ay < 8; Ay++) { + op = (11 << 12) | (Ax << 9) | (1 << 8) | (z << 6) | (1 << 3) | Ay; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'CMPM', z2, M_ripo, Ay, M_ripo, Ax, z2 == 4 ? 20 : 12, false, false); + iTab[op].f = I_CMPM; + cnt++; + } else { + BUG.say('OP EXISTS CMPM ' + op + ' ' + iTab[op].mn + ' ' + iTab[op].p.s.r + ' ' + iTab[op].p.d.r); + return false; + } + } + } + } + } + //DBcc + { + var cc, dr; + + for (cc = 0; cc < 16; cc++) { + for (dr = 0; dr < 8; dr++) { + op = (5 << 12) | (cc << 8) | (25 << 3) | dr; + + if (iTab[op].op === -1) { + iTab[op] = mkDBcc(op, 'DB' + ccNames[cc], 2, cc, -1, dr, 12, 10, 14); + iTab[op].f = I_DBcc; + cnt++; + } else { + BUG.say('OP EXISTS DBcc ' + op); + return false; + } + } + } + } + //DIVS + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (8 << 12) | (Dn << 9) | (7 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'DIVS', 2, ea[1], ea[2], M_rdd, Dn, 158, true, false); + iTab[op].f = I_DIVS; + cnt++; + } else { + BUG.say('OP EXISTS DIVS ' + op); + return false; + } + } + } + } + } + //DIVU + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (8 << 12) | (Dn << 9) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'DIVU', 2, ea[1], ea[2], M_rdd, Dn, 140, true, false); + iTab[op].f = I_DIVU; + cnt++; + } else { + BUG.say('OP EXISTS DIVU ' + op); + return false; + } + } + } + } + } + //EOR + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, z3, Dn, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + z3 = z == 0 ? 4 : (z == 1 ? 5 : 6); + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (11 << 12) | (Dn << 9) | (z3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'EOR', z2, M_rdd, Dn, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 8 : 4) : (z2 == 4 ? 12 : 8), false, true); + iTab[op].f = I_EOR; + //iTab[op].p.cyc = ea[1] == M_rdd ? (z2 == 4 ? 8 : 4) : ((z2 == 4 ? 12 : 8) + mkCyc(z2, ea[1])); + cnt++; + } else { + BUG.say('OP EXISTS EOR ' + op); + return false; + } + } + } + } + } + } + //EORI + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (10 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'EORI', z2, M_imm, -1, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 16 : 8) : (z2 == 4 ? 20 : 12), false, ea[1] != M_rdd); + iTab[op].f = I_EORI; + cnt++; + } else { + BUG.say('OP EXISTS EORI ' + op); + return false; + } + } + } + } + } + //EORI_CCR + { + op = 0xA3C; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'EORI_CCR', 1, M_imm, -1, 20, false); + iTab[op].f = I_EORI_CCR; + cnt++; + } else { + BUG.say('OP EXISTS EORI ' + op); + return false; + } + } + //EORI_SR + { + op = 0xA7C; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'EORI_SR', 2, M_imm, -1, 20, false); + iTab[op].pr = true; + iTab[op].f = I_EORI_SR; + cnt++; + } else { + BUG.say('OP EXISTS EORI ' + op); + return false; + } + } + //EXG + { + var m, opm, Rx, Ry; + + for (m = 0; m < 3; m++) { + opm = m == 0 ? 8 : (m == 1 ? 9 : 17); + for (Rx = 0; Rx < 8; Rx++) { + for (Ry = 0; Ry < 8; Ry++) { + op = (12 << 12) | (Rx << 9) | (1 << 8) | (opm << 3) | Ry; + + if (iTab[op].op === -1) { + if (m == 0) + iTab[op] = mkSD(op, 'EXG', 4, M_rdd, Rx, M_rdd, Ry, 6, false, false); + else if (m == 1) + iTab[op] = mkSD(op, 'EXG', 4, M_rda, Rx, M_rda, Ry, 6, false, false); + else + iTab[op] = mkSD(op, 'EXG', 4, M_rdd, Rx, M_rda, Ry, 6, false, false); + + iTab[op].f = I_EXG; + cnt++; + } else { + BUG.say('OP EXISTS EXG ' + op + ' ' + iTab[op].mn + ' ' + iTab[op].p.s.r + ' ' + iTab[op].p.d.r); + return false; + } + } + } + } + } + //EXT + { + var z, z2, opm, Dn; + + for (z = 1; z < 3; z++) { + z2 = z == 1 ? 2 : 4; + opm = z == 1 ? 2 : 3; + for (Dn = 0; Dn < 8; Dn++) { + op = (36 << 9) | (opm << 6) | Dn; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'EXT', z2, M_rdd, Dn, 4, false); + iTab[op].f = I_EXT; + cnt++; + } else { + BUG.say('OP EXISTS EXT ' + op + ' ' + iTab[op].mn + ' ' + iTab[op].p.s.r + ' ' + iTab[op].p.d.r); + return false; + } + } + } + } + //ILLEGAL + { + op = 0x4AFC; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'ILLEGAL'); + iTab[op].f = I_ILLEGAL; + cnt++; + } else { + BUG.say('OP EXISTS ILLEGAL ' + op); + return false; + } + } + //JMP + { + var en = [M_ria, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl]; + var mr, ea, cyc; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (315 << 6) | ea[0]; + + if (iTab[op].op === -1) { + switch (ea[1]) { + case M_ria: cyc = 8; break; + case M_rid: cyc = 10; break; + case M_rii: cyc = 14; break; + case M_pcid: cyc = 10; break; + case M_pcii: cyc = 14; break; + case M_absw: cyc = 10; break; + case M_absl: cyc = 12; break; + } + iTab[op] = mkD(op, 'JMP', 0, ea[1], ea[2], cyc, false); + iTab[op].f = I_JMP; + cnt++; + } else { + BUG.say('OP EXISTS JMP ' + op); + return false; + } + } + } + } + //JSR + { + var en = [M_ria, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl]; + var mr, ea, cyc; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (314 << 6) | ea[0]; + + if (iTab[op].op === -1) { + switch (ea[1]) { + case M_ria: cyc = 16; break; + case M_rid: cyc = 18; break; + case M_rii: cyc = 22; break; + case M_pcid: cyc = 18; break; + case M_pcii: cyc = 22; break; + case M_absw: cyc = 18; break; + case M_absl: cyc = 20; break; + } + iTab[op] = mkD(op, 'JSR', 0, ea[1], ea[2], cyc, false); + iTab[op].f = I_JSR; + cnt++; + } else { + BUG.say('OP EXISTS JSR ' + op); + return false; + } + } + } + } + //LEA + { + var en = [M_ria, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl]; + var An, mr, ea, cyc; + + for (An = 0; An < 8; An++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (4 << 12) | (An << 9) | (7 << 6) | ea[0]; + + if (iTab[op].op === -1) { + switch (ea[1]) { + case M_ria: cyc = 4; break; + case M_rid: cyc = 8; break; + case M_rii: cyc = 12; break; + case M_pcid: cyc = 8; break; + case M_pcii: cyc = 12; break; + case M_absw: cyc = 8; break; + case M_absl: cyc = 12; break; + } + iTab[op] = mkSD(op, 'LEA', 4, ea[1], ea[2], M_rda, An, cyc, false, false); + iTab[op].f = I_LEA; + cnt++; + } else { + BUG.say('OP EXISTS LEA ' + op); + return false; + } + } + } + } + } + //LINK + { + var An; + + for (An = 0; An < 8; An++) { + op = (2506 << 3) | An; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'LINK', 2, M_rda, An, M_imm, -1, 16, false, false); + iTab[op].f = I_LINK; + cnt++; + } else { + BUG.say('OP EXISTS LINK ' + op); + return false; + } + } + } + //LSL,LSR + { + var en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, dr, ir, cr, Dy, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + + for (dr = 0; dr < 2; dr++) { + for (ir = 0; ir < 2; ir++) { + for (cr = 0; cr < 8; cr++) { + for (Dy = 0; Dy < 8; Dy++) { + op = (14 << 12) | (cr << 9) | (dr << 8) | (z << 6) | (ir << 5) | (1 << 3) | Dy; + + if (iTab[op].op === -1) { + if (ir == 0) + iTab[op] = mkSD(op, dr == 0 ? 'LSR_RI' : 'LSL_RI', z2, M_imm, cr == 0 ? 8 : cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + else + iTab[op] = mkSD(op, dr == 0 ? 'LSR_RD' : 'LSL_RD', z2, M_rdd, cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + + iTab[op].f = dr == 0 ? I_LSR : I_LSL; + cnt++; + } else { + BUG.say('OP EXISTS LSx ' + op); + return false; + } + } + } + } + } + } + for (dr = 0; dr < 2; dr++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (113 << 9) | (dr << 8) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, dr == 0 ? 'LSR_M' : 'LSL_M', 2, M_imm, 1, ea[1], ea[2], 8, false, true); + iTab[op].f = dr == 0 ? I_LSR : I_LSL; + cnt++; + } else { + BUG.say('OP EXISTS LSx ' + op); + return false; + } + } + } + } + } + //MOVE + { + var tab2 = [ + [ 4,0, 8, 8, 8,12,0,0,14,12,16,0], + [ 4,0, 8, 8, 8,12,0,0,14,12,16,0], + [ 8,0,12,12,12,16,0,0,18,16,20,0], + [ 8,0,12,12,12,16,0,0,18,16,20,0], + [10,0,14,14,14,18,0,0,20,18,22,0], + [12,0,16,16,16,20,0,0,22,20,24,0], + [14,0,18,18,18,22,0,0,24,22,26,0], + [12,0,16,16,16,20,0,0,22,20,24,0], + [14,0,18,18,18,22,0,0,24,22,26,0], + [12,0,16,16,16,20,0,0,22,20,24,0], + [16,0,20,20,20,24,0,0,26,24,28,0], + [ 8,0,12,12,12,16,0,0,18,16,20,0] + ]; + var tab4 = [ + [ 4,0,12,12,12,16,0,0,18,16,20,0], + [ 4,0,12,12,12,16,0,0,18,16,20,0], + [12,0,20,20,20,24,0,0,26,24,28,0], + [12,0,20,20,20,24,0,0,26,24,28,0], + [14,0,22,22,22,26,0,0,28,26,30,0], + [16,0,24,24,24,28,0,0,30,28,32,0], + [18,0,26,26,26,30,0,0,32,30,34,0], + [16,0,24,24,24,28,0,0,30,28,32,0], + [18,0,26,26,26,30,0,0,32,30,34,0], + [16,0,24,24,24,28,0,0,30,28,32,0], + [20,0,28,28,28,32,0,0,34,32,36,0], + [12,0,20,20,20,24,0,0,26,24,28,0] + ]; + var sen = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var den = [M_rdd,M_ria, M_ripo, M_ripr, M_rid, M_rii,M_absw, M_absl]; + var z, z2, z3, smr, dmr, sea, dea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + z3 = z == 0 ? 1 : (z == 1 ? 3 : 2); + + for (dmr = 0; dmr < 64; dmr++) { + dea = mkEA(dmr, den, 1); + if (dea[0] != -1) { + for (smr = 0; smr < 64; smr++) { + sea = mkEA(smr, sen, 0); + if (sea[0] != -1) { + if (sea[1] == M_rda && z == 0) //For byte size operation, address register direct is not allowed. + continue; + + op = (z3 << 12) | (dea[0] << 6) | sea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'MOVE', z2, sea[1], sea[2], dea[1], dea[2], z2 == 4 ? tab4[sea[1]-1][dea[1]-1] : tab2[sea[1]-1][dea[1]-1], false, false); + iTab[op].f = I_MOVE; + //iTab[op].p.cyc = z2 == 4 ? tab4[sea[1]-1][dea[1]-1] : tab2[sea[1]-1][dea[1]-1]; + //if (typeof(iTab[op].p.cyc) != 'number') console.log(op, z2, sea[1], dea[1]); + cnt++; + } else { + BUG.say('OP EXISTS MOVE op ' + op + ', size ' + z2 + ', sm ' + sea[1] + ', sr ' + sea[2] + ', dm ' + dea[1] + ', dr ' + dea[2]); + return false; + } + } + } + } + } + } + } + //MOVEA + { + var tab2 = [4,4,8,8,10,12,14,12,14,12,16,8]; + var tab4 = [4,4,12,12,14,16,18,16,18,16,20,12]; + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var z, z2, z3, An, mr, ea; + + for (z = 1; z < 3; z++) { + z2 = z == 1 ? 2 : 4; + z3 = z == 1 ? 3 : 2; + + for (An = 0; An < 8; An++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (z3 << 12) | (An << 9) | (1 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'MOVEA', z2, ea[1], ea[2], M_rda, An, z2 == 4 ? tab4[ea[1]-1] : tab2[ea[1]-1], false, false); + iTab[op].f = I_MOVEA; + //iTab[op].p.cyc = z2 == 4 ? tab4[ea[1]-1] : tab2[ea[1]-1]; + cnt++; + } else { + BUG.say('OP EXISTS MOVEA op ' + op + ', size ' + z2 + ', sm ' + sea[1] + ', sr ' + sea[2] + ', dm ' + dea[1] + ', dr ' + dea[2]); + return false; + } + } + } + } + } + } + //MOVE_CCR2 ups, not for the 68000 + /*{ + var en = [M_rdd,M_ria,M_ripo,M_ripr,M_rid,M_rii,M_absw,M_absl]; + var mr, ea; + + for (mr = 0; mr < 64; mr++) + { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) + { + op = (267 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'MOVE_CCR2', 2, ea[1], ea[2]); + iTab[op].f = I_MOVE_CCR2; + cnt++; + } else { + BUG.say('OP EXISTS MOVE_CCR2 '+op); + return false; + } + } + } + }*/ + //MOVE_2CCR + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var mr, ea; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (275 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'MOVE_2CCR', 2, ea[1], ea[2], 12, ea[1] == M_rdd ? false : true); + iTab[op].f = I_MOVE_2CCR; + cnt++; + } else { + BUG.say('OP EXISTS MOVE_2CCR ' + op); + return false; + } + } + } + } + //MOVE_SR2 + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var mr, ea; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (259 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'MOVE_SR2', 2, ea[1], ea[2], ea[1] == M_rdd ? 6 : 8, ea[1] != M_rdd); + iTab[op].f = I_MOVE_SR2; + cnt++; + } else { + BUG.say('OP EXISTS MOVE_SR2 ' + op); + return false; + } + } + } + } + //MOVE_2SR + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var mr, ea; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (283 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'MOVE_2SR', 2, ea[1], ea[2], 12, ea[1] == M_rdd ? false : true); + iTab[op].pr = true; + iTab[op].f = I_MOVE_2SR; + cnt++; + } else { + BUG.say('OP EXISTS MOVE_2SR ' + op); + return false; + } + } + } + } + //MOVE_USP + { + var dr, An; + + for (dr = 0; dr < 2; dr++) { + for (An = 0; An < 8; An++) { + op = (1254 << 4) | (dr << 3) | An; + + if (iTab[op].op === -1) { + if (dr == 0) + iTab[op] = mkS(op, 'MOVE_A2USP', 4, M_rda, An, 4, false); + else + iTab[op] = mkD(op, 'MOVE_USP2A', 4, M_rda, An, 4, false); + + iTab[op].pr = true; + iTab[op].f = (dr == 0) ? I_MOVE_A2USP : I_MOVE_USP2A; + cnt++; + } else { + BUG.say('OP EXISTS MOVE_USP ' + op); + return false; + } + } + } + } + //MOVEM + { + var z, z2, dr, mr, ea, cyc; + + for (z = 0; z < 2; z++) { + z2 = z == 0 ? 2 : 4; + for (dr = 0; dr < 2; dr++) { + if (dr == 0) en = [M_ria, M_ripr, M_rid, M_rii, M_absw, M_absl]; + else en = [M_ria, M_ripo, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl]; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (9 << 11) | (dr << 10) | (1 << 7) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + if (dr == 0) { + switch (ea[1]) { + case M_ria: cyc = 8; break; + case M_ripr: cyc = 8; break; + case M_rid: cyc = 12; break; + case M_rii: cyc = 14; break; + case M_absw: cyc = 12; break; + case M_absl: cyc = 16; break; + } + iTab[op] = mkSD(op, 'MOVEM_R2M', z2, M_list, -1, ea[1], ea[2], cyc, false, false); + iTab[op].f = I_MOVEM_R2M; + } else { + switch (ea[1]) { + case M_ria: cyc = 12; break; + case M_ripo: cyc = 12; break; + case M_rid: cyc = 16; break; + case M_rii: cyc = 18; break; + case M_pcid: cyc = 16; break; + case M_pcii: cyc = 18; break; + case M_absw: cyc = 16; break; + case M_absl: cyc = 20; break; + } + iTab[op] = mkSD(op, 'MOVEM_M2R', z2, M_list, -1, ea[1], ea[2], cyc, false, false); + iTab[op].f = I_MOVEM_M2R; + } + cnt++; + } else { + BUG.say('OP EXISTS MOVEM ' + op); + return false; + } + } + } + } + } + } + //MOVEP + { + var m, opm, Dn, An; + + for (m = 0; m < 4; m++) { + opm = m + 4; + for (Dn = 0; Dn < 8; Dn++) { + for (An = 0; An < 8; An++) { + op = (Dn << 9) | (opm << 6) | (1 << 3) | An; + + if (iTab[op].op === -1) { + if (m == 0) + iTab[op] = mkSD(op, 'MOVEP', 2, M_rid, An, M_rdd, Dn, 16, false, false); + else if (m == 1) + iTab[op] = mkSD(op, 'MOVEP', 4, M_rid, An, M_rdd, Dn, 24, false, false); + else if (m == 2) + iTab[op] = mkSD(op, 'MOVEP', 2, M_rdd, Dn, M_rid, An, 16, false, false); + else + iTab[op] = mkSD(op, 'MOVEP', 4, M_rdd, Dn, M_rid, An, 24, false, false); + + iTab[op].f = I_MOVEP; + cnt++; + } else { + BUG.say('OP EXISTS MOVEP ' + op); + return false; + } + } + } + } + } + //MOVEQ + { + var Dn, d; + + for (Dn = 0; Dn < 8; Dn++) { + for (d = 0; d < 256; d++) { + op = (7 << 12) | (Dn << 9) | d; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'MOVEQ', 4, M_imm, d, M_rdd, Dn, 4, false, false); + iTab[op].f = I_MOVEQ; + cnt++; + } else { + BUG.say('OP EXISTS MOVEQ ' + op); + return false; + } + } + } + } + //MULS + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (12 << 12) | (Dn << 9) | (7 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'MULS', 2, ea[1], ea[2], M_rdd, Dn, 70, true, false); + iTab[op].f = I_MULS; + cnt++; + } else { + BUG.say('OP EXISTS MULS ' + op); + return false; + } + } + } + } + } + //MULU + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var Dn, mr, ea; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (12 << 12) | (Dn << 9) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'MULU', 2, ea[1], ea[2], M_rdd, Dn, 70, true, false); + iTab[op].f = I_MULU; + cnt++; + } else { + BUG.say('OP EXISTS MULU ' + op); + return false; + } + } + } + } + } + //NBCD + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var mr, ea; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (288 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'NBCD', 1, ea[1], ea[2], ea[1] == M_rdd ? 6 : 8, ea[1] != M_rdd); + iTab[op].f = I_NBCD; + cnt++; + } else { + BUG.say('OP EXISTS NBCD ' + op); + return false; + } + } + } + } + //NEG + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (68 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'NEG', z2, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 6 : 4) : (z2 == 4 ? 12 : 8), ea[1] != M_rdd); + iTab[op].f = I_NEG; + cnt++; + } else { + BUG.say('OP EXISTS NEG ' + op); + return false; + } + } + } + } + } + //NEGX + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (64 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'NEGX', z2, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 6 : 4) : (z2 == 4 ? 12 : 8), ea[1] != M_rdd); + iTab[op].f = I_NEGX; + cnt++; + } else { + BUG.say('OP EXISTS NEGX ' + op); + return false; + } + } + } + } + } + //NOP + { + op = 0x4E71; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'NOP', 4); + iTab[op].f = I_NOP; + cnt++; + } else { + BUG.say('OP EXISTS NOP ' + op); + return false; + } + } + //NOT + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (70 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'NOT', z2, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 6 : 4) : (z2 == 4 ? 12 : 8), ea[1] != M_rdd); + iTab[op].f = I_NOT; + cnt++; + } else { + BUG.say('OP EXISTS NOT ' + op); + return false; + } + } + } + } + } + //OR + { + var z, z2, dir, en, Dn, mr, ea, cyc; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (dir = 0; dir < 2; dir++) { + if (dir == 0) en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + else en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (8 << 12) | (Dn << 9) | (dir << 8) | (z << 6) | ea[0]; + + cyc = dir == 0 ? (z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 4)) : (z2 == 4 ? 12 : 8); + + if (iTab[op].op === -1) { + if (dir == 0) + iTab[op] = mkSD(op, 'OR', z2, ea[1], ea[2], M_rdd, Dn, cyc, true, false); + else + iTab[op] = mkSD(op, 'OR', z2, M_rdd, Dn, ea[1], ea[2], cyc, false, true); + + iTab[op].f = I_OR; + //iTab[op].p.cyc = (dir == 0 ? (z2 == 4 ? (ea[1]==M_rdd||ea[1]==M_imm?8:6) : (ea[1]==M_rdd||ea[1]==M_imm?8:4)) : (z2 == 4 ? 12 : 8)) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS OR ' + op); + return false; + } + } + } + } + } + } + } + //ORI + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'ORI', z2, M_imm, -1, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 16 : 8) : (z2 == 4 ? 20 : 12), false, ea[1] != M_rdd); + iTab[op].f = I_ORI; + cnt++; + } else { + BUG.say('OP EXISTS ORI ' + op); + return false; + } + } + } + } + } + //ORI_CCR + { + op = 0x3C; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'ORI_CCR', 1, M_imm, -1, 20, false); + iTab[op].f = I_ORI_CCR; + cnt++; + } else { + BUG.say('OP EXISTS ORI_CCR ' + op); + return false; + } + } + //ORI_SR + { + op = 0x7C; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'ORI_SR', 2, M_imm, -1, 20, false); + iTab[op].pr = true; + iTab[op].f = I_ORI_SR; + cnt++; + } else { + BUG.say('OP EXISTS ORI_SR ' + op); + return false; + } + } + //PEA + { + var en = [M_ria, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl]; + var mr, ea, cyc; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (289 << 6) | ea[0]; + + if (iTab[op].op === -1) { + switch (ea[1]) { + case M_ria: cyc = 12; break; + case M_rid: cyc = 16; break; + case M_rii: cyc = 20; break; + case M_pcid: cyc = 16; break; + case M_pcii: cyc = 20; break; + case M_absw: cyc = 16; break; + case M_absl: cyc = 20; break; + } + iTab[op] = mkS(op, 'PEA', 4, ea[1], ea[2], cyc, false); + iTab[op].f = I_PEA; + cnt++; + } else { + BUG.say('OP EXISTS PEA ' + op); + return false; + } + } + } + } + //RESET + { + op = 0x4E70; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'RESET', 132); + iTab[op].f = I_RESET; + cnt++; + } else { + BUG.say('OP EXISTS RESET ' + op); + return false; + } + } + //ROL,ROR + { + var en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, dr, ir, cr, Dy, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + + for (dr = 0; dr < 2; dr++) { + for (ir = 0; ir < 2; ir++) { + for (cr = 0; cr < 8; cr++) { + for (Dy = 0; Dy < 8; Dy++) { + op = (14 << 12) | (cr << 9) | (dr << 8) | (z << 6) | (ir << 5) | (3 << 3) | Dy; + + if (iTab[op].op === -1) { + if (ir == 0) + iTab[op] = mkSD(op, dr == 0 ? 'ROR_RI' : 'ROL_RI', z2, M_imm, cr == 0 ? 8 : cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + else + iTab[op] = mkSD(op, dr == 0 ? 'ROR_RD' : 'ROL_RD', z2, M_rdd, cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + + iTab[op].f = dr == 0 ? I_ROR : I_ROL; + cnt++; + } else { + BUG.say('OP EXISTS ROx ' + op); + return false; + } + } + } + } + } + } + for (dr = 0; dr < 2; dr++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (115 << 9) | (dr << 8) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, dr == 0 ? 'ROR_M' : 'ROL_M', 2, M_imm, 1, ea[1], ea[2], 8, false, true); + iTab[op].f = dr == 0 ? I_ROR : I_ROL; + cnt++; + } else { + BUG.say('OP EXISTS ROx ' + op); + return false; + } + } + } + } + } + //ROXL,ROXR + { + var en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, dr, ir, cr, Dy, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + + for (dr = 0; dr < 2; dr++) { + for (ir = 0; ir < 2; ir++) { + for (cr = 0; cr < 8; cr++) { + for (Dy = 0; Dy < 8; Dy++) { + op = (14 << 12) | (cr << 9) | (dr << 8) | (z << 6) | (ir << 5) | (2 << 3) | Dy; + + if (iTab[op].op === -1) { + if (ir == 0) + iTab[op] = mkSD(op, dr == 0 ? 'ROXR_RI' : 'ROXL_RI', z2, M_imm, cr == 0 ? 8 : cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + else + iTab[op] = mkSD(op, dr == 0 ? 'ROXR_RD' : 'ROXL_RD', z2, M_rdd, cr, M_rdd, Dy, z2 == 4 ? 8 : 6, false, false); + + iTab[op].f = dr == 0 ? I_ROXR : I_ROXL; + cnt++; + } else { + BUG.say('OP EXISTS ROx ' + op); + return false; + } + } + } + } + } + } + for (dr = 0; dr < 2; dr++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (114 << 9) | (dr << 8) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, dr == 0 ? 'ROXR_M' : 'ROXL_M', 2, M_imm, 1, ea[1], ea[2], 8, false, true); + iTab[op].f = dr == 0 ? I_ROXR : I_ROXL; + cnt++; + } else { + BUG.say('OP EXISTS ROx ' + op); + return false; + } + } + } + } + } + //RTE + { + op = 0x4E73; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'RTE', 20); + iTab[op].pr = true; + iTab[op].f = I_RTE; + cnt++; + } else { + BUG.say('OP EXISTS RTE ' + op); + return false; + } + } + //RTR + { + op = 0x4E77; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'RTR', 20); + iTab[op].f = I_RTR; + cnt++; + } else { + BUG.say('OP EXISTS RTR ' + op); + return false; + } + } + //RTS + { + op = 0x4E75; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'RTS', 16); + iTab[op].f = I_RTS; + cnt++; + } else { + BUG.say('OP EXISTS RTS ' + op); + return false; + } + } + //SBCD + { + var rm, Rx, Ry; + + for (rm = 0; rm < 2; rm++) { + for (Rx = 0; Rx < 8; Rx++) { + for (Ry = 0; Ry < 8; Ry++) { + op = (8 << 12) | (Ry << 9) | (1 << 8) | (rm << 3) | Rx; + + if (iTab[op].op === -1) { + if (rm == 0) + iTab[op] = mkSD(op, 'SBCD', 1, M_rdd, Rx, M_rdd, Ry, 6, false, false); + else + iTab[op] = mkSD(op, 'SBCD', 1, M_ripr, Rx, M_ripr, Ry, 18, false, false); + + iTab[op].f = I_SBCD; + cnt++; + } else { + BUG.say('OP EXISTS SBCD ' + op); + return false; + } + } + } + } + } + //Scc + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var cc, mr, ea; + + for (cc = 0; cc < 16; cc++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (5 << 12) | (cc << 8) | (3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkCD(op, 'S' + ccNames[cc], 1, cc, -1, -1, ea[1], ea[2], ea[1] == M_rdd ? 6 : 8, ea[1] == M_rdd ? 4 : 8, ea[1] != M_rdd); + iTab[op].f = I_Scc; + cnt++; + } else { + BUG.say('OP EXISTS S' + ccNames[cc] + ' ' + op); + return false; + } + } + } + } + } + //STOP + { + op = 0x4E72; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'STOP', 2, M_imm, -1, 4, false); + iTab[op].pr = true; + iTab[op].f = I_STOP; + cnt++; + } else { + BUG.say('OP EXISTS STOP ' + op); + return false; + } + } + //SUB + { + var z, z2, dir, en, Dn, mr, ea, cyc; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (dir = 0; dir < 2; dir++) { + if (dir == 0) en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + else en = [M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + + for (Dn = 0; Dn < 8; Dn++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + if (dir == 0 && ea[1] == M_rda && z == 0) //For byte-sized operation, address register direct is not allowed + continue; + + op = (9 << 12) | (Dn << 9) | (dir << 8) | (z << 6) | ea[0]; + + cyc = dir == 0 ? (z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : 4) : (z2 == 4 ? 12 : 8); + + if (iTab[op].op === -1) { + if (dir == 0) + iTab[op] = mkSD(op, 'SUB', z2, ea[1], ea[2], M_rdd, Dn, cyc, true, false); + else + iTab[op] = mkSD(op, 'SUB', z2, M_rdd, Dn, ea[1], ea[2], cyc, false, true); + + iTab[op].f = I_SUB; + //iTab[op].p.cyc = ((dir == 0) ? (z2 == 4 ? (ea[1]==M_rdd||ea[1]==M_imm?8:6) : 4) : (z2 == 4 ? 12 : 8)) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS SUB ' + op); + return false; + } + } + } + } + } + } + } + //SUBA + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_pcid, M_pcii, M_absw, M_absl, M_imm]; + var z, z2, z3, An, mr, ea; + + for (z = 0; z < 2; z++) { + z2 = z == 0 ? 2 : 4; + z3 = z == 0 ? 3 : 7; + for (An = 0; An < 8; An++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (9 << 12) | (An << 9) | (z3 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'SUBA', z2, ea[1], ea[2], M_rda, An, z2 == 4 ? (ea[1] == M_rdd || ea[1] == M_imm ? 8 : 6) : 8, true, false); + iTab[op].f = I_SUBA; + //iTab[op].p.cyc = (z2 == 4 ? (ea[1]==M_rdd||ea[1]==M_imm?8:6) : 8) + mkCyc(z2, ea[1]); + cnt++; + } else { + BUG.say('OP EXISTS SUBA ' + op); + return false; + } + } + } + } + } + } + //SUBI + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (4 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'SUBI', z2, M_imm, -1, ea[1], ea[2], ea[1] == M_rdd ? (z2 == 4 ? 16 : 8) : (z2 == 4 ? 20 : 12), false, ea[1] != M_rdd); + iTab[op].f = I_SUBI; + cnt++; + } else { + BUG.say('OP EXISTS SUBI ' + op); + return false; + } + } + } + } + } + //SUBQ + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var z, z2, id, mr, ea, cyc; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (id = 0; id < 8; id++) { + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + if (ea[1] == M_rda && z == 0) continue; //An word and long only + + op = (5 << 12) | (id << 9) | (1 << 8) | (z << 6) | ea[0]; + cyc = ea[1] == M_rda ? 8 : (ea[1] == M_rdd ? (z2 == 4 ? 8 : 4) : (z2 == 4 ? 12 : 8)); + + if (iTab[op].op === -1) { + iTab[op] = mkSD(op, 'SUBQ', z2, M_imm, id == 0 ? 8 : id, ea[1], ea[2], cyc, false, ea[1] != M_rdd && ea[1] != M_rda); + iTab[op].f = I_SUBQ; + cnt++; + } else { + BUG.say('OP EXISTS SUBQ ' + op); + return false; + } + } + } + } + } + } + //SUBX + { + var z, z2, rm, Rx, Ry; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (rm = 0; rm < 2; rm++) { + for (Rx = 0; Rx < 8; Rx++) { + for (Ry = 0; Ry < 8; Ry++) { + op = (9 << 12) | (Ry << 9) | (1 << 8) | (z << 6) | (rm << 3) | Rx; + + if (iTab[op].op === -1) { + if (rm == 0) + iTab[op] = mkSD(op, 'SUBX', z2, M_rdd, Rx, M_rdd, Ry, z2 == 4 ? 8 : 4, false, false); + else + iTab[op] = mkSD(op, 'SUBX', z2, M_ripr, Rx, M_ripr, Ry, z2 == 4 ? 30 : 18, false, false); + + iTab[op].f = I_SUBX; + cnt++; + } else { + BUG.say('OP EXISTS SUBX ' + op + ' ' + iTab[op].mn + ' ' + iTab[op].p.s.r + ' ' + iTab[op].p.d.r); + return false; + } + } + } + } + } + } + //SWAP + { + var Dn; + + for (Dn = 0; Dn < 8; Dn++) { + op = (2312 << 3) | Dn; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'SWAP', 2, M_rdd, Dn, 4, false); + iTab[op].f = I_SWAP; + cnt++; + } else { + BUG.say('OP EXISTS SWAP ' + op); + return false; + } + } + } + //TAS + { + var en = [M_rdd, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl]; + var mr, ea; + + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (299 << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'TAS', 1, ea[1], ea[2], ea[1] == M_rdd ? 4 : 10, ea[1] != M_rdd); + iTab[op].f = I_TAS; + cnt++; + } else { + BUG.say('OP EXISTS TAS ' + op); + return false; + } + } + } + } + //TRAP + { + var v; + + for (v = 0; v < 16; v++) { + op = (1252 << 4) | v; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'TRAP', 0, M_imm, v, 38, false); + iTab[op].f = I_TRAP; + cnt++; + } else { + BUG.say('OP EXISTS TRAP ' + op); + return false; + } + } + } + //TRAPV + { + op = 0x4E76; + + if (iTab[op].op === -1) { + iTab[op] = mkN(op, 'TRAPV', 4); + iTab[op].f = I_TRAPV; + cnt++; + } else { + BUG.say('OP EXISTS TRAPV ' + op); + return false; + } + } + //TST + { + var en = [M_rdd, M_rda, M_ria, M_ripo, M_ripr, M_rid, M_rii, M_absw, M_absl, M_imm]; + var z, z2, mr, ea; + + for (z = 0; z < 3; z++) { + z2 = z == 0 ? 1 : (z == 1 ? 2 : 4); + for (mr = 0; mr < 64; mr++) { + ea = mkEA(mr, en, 0); + if (ea[0] != -1) { + op = (74 << 8) | (z << 6) | ea[0]; + + if (iTab[op].op === -1) { + iTab[op] = mkD(op, 'TST', z2, ea[1], ea[2], 4, ea[1] != M_rdd && ea[1] != M_rda); + iTab[op].f = I_TST; + cnt++; + } else { + BUG.say('OP EXISTS TST ' + op); + return false; + } + } + } + } + } + //UNLK + { + var An; + + for (An = 0; An < 8; An++) { + op = (2507 << 3) | An; + + if (iTab[op].op === -1) { + iTab[op] = mkS(op, 'UNLK', 0, M_rda, An, 12, false); + iTab[op].f = I_UNLK; + cnt++; + } else { + BUG.say('OP EXISTS UNLK ' + op); + return false; + } + } + } + + //for (op = 0; op < 0x10000; op++) if (iTab[op].op !== -1 && !(iTab[op].p.cyc || iTab[op].p.cycTaken || iTab[op].p.cycTrue || iTab[op].p.cycFalse || typeof(iTab[op].p.cyc) == 'number')) console.log(iTab[op].mn, iTab[op].p.z); + + BUG.say(sprintf('cpu.mkiTab() build %d instructions', cnt)); + return true; + } + /* ...end of the fun part. */ + + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + + function printIdx(base, ar, pc) { + var ext = AMIGA.mem.load16(pc); + var disp = castByte(ext & 0xff); + var r = (ext & 0x7000) >>> 12; + var idx = (ext & 0x8000) ? regs.a[r] : regs.d[r]; + if (ext & 0x800) idx = castLong(idx); + else idx = castWord(idx & 0xffff); + var addr = (base + disp + idx); + if (ar != -1) + return sprintf('(%d,A%d,%s%d)[$%08x]', disp, ar, (ext & 0x8000) ? 'A' : 'D', r, addr); + else + return sprintf('(%d,PC,%s%d)[$%08x]', disp, (ext & 0x8000) ? 'A' : 'D', r, addr); + } + + function printEA(ea, z, m, pc) { + var dp, o = ' '; + + switch (ea.m) { + case M_rdd: + o += sprintf('D%d', ea.r); + break; + case M_rda: + o += sprintf('A%d', ea.r); + break; + case M_ria: + o += sprintf('(A%d)', ea.r); + break; + case M_ripo: + o += sprintf('(A%d)+', ea.r); + break; + case M_ripr: + o += sprintf('-(A%d)', ea.r); + break; + case M_rid: + dp = castWord(AMIGA.mem.load16(pc)); pc += 2; + o += sprintf('$%04x(A%d)[$%08x]', dp, ea.r, regs.a[ea.r] + dp); + break; + case M_rii: + o += printIdx(regs.a[ea.r], ea.r, pc); + break; + case M_pcid: + dp = castWord(AMIGA.mem.load16(pc)); + o += sprintf('$%04x(PC)[$%08x]', dp, pc + dp); + pc += 2; + break; + case M_pcii: + o += printIdx(pc, -1, pc); pc += 2; + break; + case M_absw: + dp = castWord(AMIGA.mem.load16(pc)); pc += 2; + o += sprintf('($%04x)', dp); + break; + case M_absl: + dp = AMIGA.mem.load32(pc); pc += 4; + o += sprintf('($%08x)', dp); + break; + case M_imm: { + if (ea.r == -1) { + switch (z) { + case 1: + dp = castByte(AMIGA.mem.load16(pc)); pc += 2; + o += sprintf('#$%02x', dp & 0xff); + break; + case 2: + dp = castWord(AMIGA.mem.load16(pc)); pc += 2; + o += sprintf('#$%04x', dp); + break; + case 4: + dp = castLong(AMIGA.mem.load32(pc)); pc += 4; + o += sprintf('#$%08x', dp); + break; + } + } else + o += sprintf('#$%02x', castByte(ea.r)); + break; + } + case M_list: + dp = AMIGA.mem.load16(pc); pc += 2; + o += sprintf('#$%04x', dp) + ' ['+regsStr(dp, m == M_ripr)+']'; + break; + } + return [o, pc]; + } + + function printC(c, pc) { + var o = ' '; + + if (c.dp != -1) { + if (c.dp == 0) { + var dp = castWord(AMIGA.mem.load16(pc)); + o += sprintf('$%08x', pc + dp); + pc += 2; + } + /*else if (c.dp == 0xff) { //68020 + var dp = castLong(AMIGA.mem.load32(pc)); + o += sprintf('$%08x', pc + dp); + pc += 4; + }*/ + else { + var dp = castByte(c.dp); + o += sprintf('$%08x', pc + dp); + } + } else { + var dp = castWord(AMIGA.mem.load16(pc)); + o += sprintf('D%d,$%08x', c.dr, pc + dp); + pc += 2; + } + return [o, pc]; + } + + function printI(i, pc) { + var o = i.mn; + + if (o == 'ILLEGAL') return [o, pc]; + + if (i.p.z) o += '.' + szChr(i.p.z); + o += ' '; + if (i.p.s) { + var ip = printEA(i.p.s, i.p.z, i.p.d ? i.p.d.m : 0, pc); + o += ip[0]; + pc = ip[1]; + } + if (i.p.s && i.p.d) o += ','; + if (i.p.d) { + var ip = printEA(i.p.d, i.p.z, i.p.s ? i.p.s.m : 0, pc); + o += ip[0]; + pc = ip[1]; + } + if (i.p.c) { + var ip = printC(i.p.c, pc); + o += ip[0]; + pc = ip[1]; + } + return [o, pc]; + } + + this.diss = function (offset, limit) { + var pc = offset === null ? regs.pc : offset; + var cnt = 0; + + while (cnt++ < limit) { + var o = ''; + + o += sprintf('$%08x: ', pc); + for (var i = 0; i < 5; i++) + o += sprintf('$%04x ', AMIGA.mem.load16(pc + i * 2)); + + var op = AMIGA.mem.load16(pc); + pc += 2; + + var ip = printI(iTab[op], pc); + o += ip[0]; + pc = ip[1]; + + BUG.say(o); + } + } + this.dissFault = function (limit) { + this.diss(fault.pc, limit); + } + + /*function nextIWordData(data, pc) { + return (data[pc] << 8) | data[pc + 1]; + } + function nextILongData(data, pc) { + return (data[pc] << 24) | (data[pc + 1] << 16) | (data[pc + 2] << 8) | data[pc + 3]; + } + function printIdxData(data, base, ar, pc) { + var ext = nextIWordData(data, pc); + var dp8 = castByte(ext & 0xff); + var r = (ext & 0x7000) >>> 12; + var idx = (ext & 0x8000) ? regs.a[r] : regs.d[r]; + if (ext & 0x800) idx = castLong(idx); + else idx = castWord(idx & 0xffff); + //dispreg <<= (dp >> 9) & 3; //68020 + var addr = (base + dp8 + idx); + if (ar != -1) + //return sprintf('(A%d,%s%d,%02x[$%08x][%s])', ar, (dp & 0x8000)?'A':'D', r, disp8, addr, (dp & 0x800)?'L':'W'); + return sprintf('(%d,A%d,%s%d)[$%08x]', dp8, ar, (ext & 0x8000) ? 'A' : 'D', r, addr); + else + //return sprintf('(PC($%08x),%s%d,%02x[$%08x][%s])', base, (dp & 0x8000)?'A':'D', r, disp8, addr, (dp & 0x800)?'L':'W'); + return sprintf('(%d,PC,%s%d)[$%08x]', dp8, (ext & 0x8000) ? 'A' : 'D', r, addr); + } + + function printEAData(data, ea, z, pc) { + var dp, o = ' '; + + switch (ea.m) { + case M_rdd: + o += sprintf('D%d', ea.r); + break; + case M_rda: + o += sprintf('A%d', ea.r); + break; + case M_ria: + o += sprintf('(A%d)', ea.r); + break; + case M_ripo: + o += sprintf('(A%d)+', ea.r); + break; + case M_ripr: + o += sprintf('-(A%d)', ea.r); + break; + case M_rid: + dp = castWord(nextIWordData(data, pc)); pc += 2; + o += sprintf('($%04x,A%d)[$%08x]', dp, ea.r, regs.a[ea.r] + dp); + break; + case M_rii: + o += printIdxData(data, regs.a[ea.r], ea.r, pc); + break; + case M_pcid: + dp = castWord(nextIWordData(data, pc)); pc += 2; + o += sprintf('($%04x,PC)[$%08x]', dp, pc + dp); + break; + case M_pcii: + o += printIdxData(data, pc, - 1, pc); pc += 2; + break; + case M_absw: + dp = nextIWordData(data, pc); pc += 2; + o += sprintf('($%04x).W', dp); + break; + case M_absl: + dp = nextILongData(data, pc); pc += 4; + o += sprintf('($%08x).L', dp); + break; + case M_imm: { + if (ea.r == -1) { + switch (z) { + case 1: + dp = castByte(nextIWordData(data, pc)); pc += 2; + o += sprintf('#<$%02x>', dp & 0xff); + break; + case 2: + dp = castWord(nextIWordData(data, pc)); pc += 2; + o += sprintf('#<$%04x>', dp); + break; + case 4: + dp = castLong(nextILongData(data, pc)); pc += 4; + o += sprintf('#<$%08x>', dp); + break; + } + } else + o += sprintf('#<$%02x>', castByte(ea.r)); + break; + } + case M_list: + dp = nextIWordData(data, pc); pc += 2; + o += sprintf('[$%04x]', dp); + break; + } + return [o, pc]; + } + + function printCData(data, c, pc) { + var o = ' '; + + if (c.dp != -1) { + if (c.dp == 0) { + var dp = castWord(nextIWordData(data, pc)); + o += sprintf('$%08x', pc + dp); + pc += 2; + } + else { + var dp = castByte(c.dp); + o += sprintf('$%08x', pc + dp); + } + } else { + var dp = castWord(nextIWordData(data, pc)); + o += sprintf('D%d,$%08x', c.dr, pc + dp); + pc += 2; + } + return [o, pc]; + } + + function printIData(data, i, pc) { + var o = i.mn; + + if (o == 'ILLEGAL') return [o, pc]; + + if (i.p.z) o += '.' + szChr(i.p.z); + o += ' '; + if (i.p.s) { + var ip = printEAData(data, i.p.s, i.p.z, pc); + o += ip[0]; + pc = ip[1]; + } + if (i.p.s && i.p.d) o += ','; + if (i.p.d) { + var ip = printEAData(data, i.p.d, i.p.z, pc); + o += ip[0]; + pc = ip[1]; + } + if (i.p.c) { + var ip = printCData(data, i.p.c, pc); + o += ip[0]; + pc = ip[1]; + } + return [o, pc]; + } + this.dissData = function (data, limit) { + var pc = 0; + var cnt = 0; + + while (cnt++ < limit) { + var o = ''; + + o += sprintf('$%08x: ', pc); + for (var i = 0; i < 5; i++) + o += sprintf('$%04x ', nextIWordData(data, pc+i*2)); + + var op = nextIWordData(data, pc); + pc += 2; + + var ip = printIData(data, iTab[op], pc); + o += ip[0]; + pc = ip[1]; + + BUG.say(o); + } + }*/ + + function getName(addr) + { + var c, p = 0, n = ''; + while ((c = AMIGA.mem.load8(addr + p))) { + n += String.fromCharCode(c); + if (p++ > 100) return ''; + } + return n; + } + + function getTaskName(task) { + return getName(AMIGA.mem.load32(task + 10)); + } + + this.getThisTaskName = function() { + var tn = ''; + /* Extract current task-name form SysBase */ + var sysBase = AMIGA.mem.load32(4); + if (sysBase == 0x000676 || sysBase == 0xc00276 || sysBase == 0xc00a88 || sysBase == 0xc00560) { + var thisTask = AMIGA.mem.load32(sysBase + 276); + if (thisTask) + tn = getTaskName(thisTask); + } + return tn; + } + + this.dump = function() { + var i, out = '', tn = 1 ? this.getThisTaskName() : ''; + + for (i = 0; i < 8; i++) { + out += sprintf('D%d $%08x ', i, regs.d[i]); //if ((i & 3) == 3) out += '
'; + } + //out += '
'; + out += "\n"; + for (i = 0; i < 8; i++) { + out += sprintf('A%d $%08x ', i, regs.a[i]); //if ((i & 3) == 3) out += '
'; + } + //out += '
'; + out += "\n"; + out += sprintf('PC $%08x USP $%08x ISP $%08x ', regs.pc, regs.usp, regs.isp); + out += sprintf('T=%d S=%d X=%d N=%d Z=%d V=%d C=%d IMASK=%d, LTASK=%s', regs.t ? 1 : 0, regs.s ? 1 : 0, regs.x ? 1 : 0, regs.n ? 1 : 0, regs.z ? 1 : 0, regs.v ? 1 : 0, regs.c ? 1 : 0, regs.intmask, tn); + out += "\n"; + out += "\n"; + BUG.say(out); + } + + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + + /*function superState() + { + if (!regs.s) { + regs.s = true; + //regs.t = false; + var temp = regs.usp; + regs.usp = regs.a[7]; + regs.a[7] = temp; + BUG.col = 2; + } + } + + function userState(s) + { + if (s) { + var temp = regs.usp; + regs.usp = regs.a[7]; + regs.a[7] = temp; + BUG.col = 1; + } + }*/ + + function getCCR() { + return (((regs.x ? 1 : 0) << 4) | ((regs.n ? 1 : 0) << 3) | ((regs.z ? 1 : 0) << 2) | ((regs.v ? 1 : 0) << 1) | (regs.c ? 1 : 0)); + } + + function setCCR(ccr) { + regs.x = ((ccr >> 4) & 1) == 1; + regs.n = ((ccr >> 3) & 1) == 1; + regs.z = ((ccr >> 2) & 1) == 1; + regs.v = ((ccr >> 1) & 1) == 1; + regs.c = (ccr & 1) == 1; + } + + function getSR() { + return (((regs.t ? 1 : 0) << 15) | ((regs.s ? 1 : 0) << 13) | (regs.intmask << 8) | ((regs.x ? 1 : 0) << 4) | ((regs.n ? 1 : 0) << 3) | ((regs.z ? 1 : 0) << 2) | ((regs.v ? 1 : 0) << 1) | (regs.c ? 1 : 0)); + } + + function setSR(sr) { + regs.x = ((sr >> 4) & 1) == 1; + regs.n = ((sr >> 3) & 1) == 1; + regs.z = ((sr >> 2) & 1) == 1; + regs.v = ((sr >> 1) & 1) == 1; + regs.c = (sr & 1) == 1; + + var t = ((sr >> 15) & 1) == 1; + var s = ((sr >> 13) & 1) == 1; + var intmask = ((sr >> 8) & 7); + + if (regs.t == t && regs.s == s && regs.intmask == intmask) { + //BUG.say('cpu.setSR() mode ok!'); + return; + } + + var olds = regs.s; + regs.t = t; + regs.s = s; + regs.intmask = intmask; + + if (regs.s != olds) { + //BUG.say(sprintf('cpu.setSR() mode switch %s', olds ? 'userstate' : 'superstate')); + //userState(olds); + + if (olds) { + regs.isp = regs.a[7]; + regs.a[7] = regs.usp; + + BUG.col = 1; + } else { + BUG.say('cpu.setSR() mode switch to superstate!'); + + regs.usp = regs.a[7]; + regs.a[7] = regs.isp; + + BUG.col = 2; + } + } + + AMIGA.doint(); + //if (regs.t1 || regs.t0) + if (regs.t) + set_special(SPCFLAG_TRACE); + else + /* Keep SPCFLAG_DOTRACE, we still want a trace exception for SR-modifying instructions (including STOP). */ + clr_special(SPCFLAG_TRACE); + } + + function setPC(pc) { + if (pc & 1) { + BUG.say(sprintf('cpu.setPC() ADDRESS ERROR pc $%08x', pc)); + AMIGA.cpu.diss(fault.pc, 1); + //AMIGA.cpu.dump(); + exception3(pc, 0); + } + else if (pc > 0xffffff) { + BUG.say(sprintf('cpu.setPC() BUS ERROR, $%08x > 24bit, reducing address to $%08x', pc, pc & 0xffffff)); + AMIGA.cpu.diss(fault.pc, 1); + //AMIGA.cpu.dump(); + //exception2(pc, 0); + pc &= 0xffffff; + } + else if (pc < 4) { + BUG.say(sprintf('cpu.setPC() BUS ERROR pc $%08x', pc)); + AMIGA.cpu.diss(fault.pc, 1); + //AMIGA.cpu.dump(); + //exception2(pc, 0); + //AMIGA.state = 0; + } + regs.pc = pc; + } + + function exception_trace(n) { + clr_special(SPCFLAG_TRACE | SPCFLAG_DOTRACE); + //if (regs.t1 && !regs.t0) { + if (regs.t) { + /* trace stays pending if exception is div by zero, chk, trapv or trap #x */ + if (n == 5 || n == 6 || n == 7 || (n >= 32 && n <= 47)) + set_special(SPCFLAG_DOTRACE); + } + //regs.t1 = regs.t0 = regs.m = 0; + regs.t = 0; + } + + function exception(n) { + //BUG.say(sprintf('cpu.exception() nr %d', n)); + var c; + if (n < 16) + switch (n) { + case 0: c = 40; break; //Reset Initial Interrupt Stack Pointer + case 1: c = 40; break; //Reset Initial Program Counter + case 2: c = 50; break; //Access Fault + case 3: c = 50; break; //Address Error + case 4: c = 34; break; //Illegal Instruction + case 5: c = 42; break; //Integer Divide by Zero + case 6: c = 44; break; //CHK, CHK2 Instruction + case 7: c = 34; break; //FTRAPcc, TRAPcc, TRAPV Instructions + case 8: c = 34; break; //Privilege Violation + case 9: c = 34; break; //Trace + case 10: c = 34; break; //Line 1010 Emulator (Unimplemented A- Line Opcode) + case 11: c = 34; break; //Line 1111 Emulator (Unimplemented F-Line Opcode) + } + else if (n >= 24 && n < 32) + c = 44 + 4; + else if (n >= 32 && n < 48) + c = 38; + else { + BUG.say(sprintf('cpu.exception() no cycle for %d', n)); + c = 4; + } + var olds = regs.s; + + if (n >= 24 && n < 24 + 8) { + var oldn = n; + n = AMIGA.mem.load8(0x00fffff1 | (n << 1)); + if (n != oldn) BUG.say(sprintf('cpu.exception() exception from %d to %d', oldn, n)); + } + + var sr = getSR(); + //superState(); + if (!regs.s) { + regs.s = true; + regs.usp = regs.a[7]; + regs.a[7] = regs.isp; + + BUG.col = 2; + } + + if (n == 2) { + BUG.say(sprintf('cpu.exception() %d, regs.pc $%08x, fault.pc $%08x, fault.op $%04x, fault.ad $%08x, fault.ia %d', n, regs.pc, fault.pc, fault.op, fault.ad, fault.ia ? 1 : 0)); + + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, regs.pc); + stEA(exEA(new effAddr(M_ripr, 7), 2), 2, sr); + } else if (n == 3) { + BUG.say(sprintf('cpu.exception() %d, regs.pc $%08x, fault.pc $%08x, fault.op $%04x, fault.ad $%08x, fault.ia %d', n, regs.pc, fault.pc, fault.op, fault.ad, fault.ia ? 1 : 0)); + + var ia = fault.ia; + var wa = 0; + var cd = (wa ? 0 : 16) | (olds ? 4 : 0) | (ia ? 2 : 1); + + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, fault.pc); + stEA(exEA(new effAddr(M_ripr, 7), 2), 2, sr); + stEA(exEA(new effAddr(M_ripr, 7), 2), 2, fault.op); + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, fault.ad); + stEA(exEA(new effAddr(M_ripr, 7), 2), 2, cd); + } else { + stEA(exEA(new effAddr(M_ripr, 7), 4), 4, regs.pc); + stEA(exEA(new effAddr(M_ripr, 7), 2), 2, sr); + } + + var pc = AMIGA.mem.load32(n * 4); + if (pc & 1) { + BUG.say(sprintf('cpu.exception() ADDRESS ERROR pc $%08x', pc)); + if (n == 2 || n == 3) { + AMIGA.reset(); + throw new Error('double address/bus-error'); + } else + exception3(pc, 0); + } + /*else if (pc > 0xffffff) { + BUG.say(sprintf('cpu.exception() BUS ERROR pc $%08x', pc)); + //AMIGA.cpu.diss(fault.pc, 1); + //AMIGA.cpu.dump(); + exception2(pc, 0); + }*/ + regs.pc = pc; + + exception_trace(n); + return c; + } + + function exception2(ad) { + fault.ad = ad; + fault.ia = 0; + throw new Exception23(2); + } + + function exception3(ad, ia) { + fault.ad = ad; + fault.ia = ia; + throw new Exception23(3); + } + + function cycle_real() { + //if (doDiss) AMIGA.cpu.diss(null, 1); + + var op = nextIWord(); + fault.pc = regs.pc - 2; + fault.op = op; + + var cycles; + try { + cycles = iTab[op].f(iTab[op].p); + } catch (e) { + if (e instanceof Exception23) { + //BUG.info('cpu.cycle() USER EXCEPTION [%d]', e.num); + cycles = exception(e.num); + } + else if (e instanceof Error) { + BUG.info('cpu.cycle() ERROR EXCEPTION [%s]', e); + cycles = 4; + } else { + BUG.info('cpu.cycle() UNKNOWN EXCEPTION [%s]', e); + cycles = 4; + } + } + //if (doDiss) { if (doDiss) doDiss--; AMIGA.cpu.dump(); } + + return cycles >> 1; + } + + function interrupt(nr) { + regs.stopped = false; + clr_special(SPCFLAG_STOP); + //assert(nr < 8 && nr >= 0); + + exception(nr + 24); + + regs.intmask = nr; + AMIGA.doint(); + } + + var last_trace_ad = 0; + function trace() { + /*if (regs.t0 && AMIGA.config.cpu.model >= 68020) { + } else if (regs.t1) { + last_trace_ad = regs.pc; + clr_special(SPCFLAG_TRACE); + set_special(SPCFLAG_DOTRACE); + }*/ + if (regs.t) { + last_trace_ad = regs.pc; + clr_special(SPCFLAG_TRACE); + set_special(SPCFLAG_DOTRACE); + } + } + + function cycle_spc(cycles) { + if (AMIGA.spcflags & SPCFLAG_COPPER) + AMIGA.copper.cycle(); + + while ((AMIGA.spcflags & SPCFLAG_BLTNASTY) && AMIGA.dmaen(DMAF_BLTEN) && cycles > 0 && !AMIGA.config.blitter.exact) { + var c = AMIGA.blitter.blitnasty(); + if (c < 0) { + break; + } else if (c > 0) { + cycles -= c * CYCLE_UNIT * 2; + if (cycles < CYCLE_UNIT) + cycles = 0; + } else { + c = 4; + } + /*if (c == 0) { //old blitter + cycles -= 2 * CYCLE_UNIT; + if (cycles < CYCLE_UNIT) cycles = 0; + c = 1; + }*/ + AMIGA.events.cycle(c * CYCLE_UNIT); + if (AMIGA.spcflags & SPCFLAG_COPPER) AMIGA.copper.cycle(); + } + + /*if (AMIGA.spcflags & SPCFLAG_DOTRACE) + exception(9); + + if (AMIGA.spcflags & SPCFLAG_TRAP) { + clr_special(SPCFLAG_TRAP); + exception(3); + }*/ + + while (AMIGA.spcflags & SPCFLAG_STOP) { + AMIGA.events.cycle (AMIGA.config.cpu.exact ? 2 * CYCLE_UNIT : 4 * CYCLE_UNIT); + + if (AMIGA.spcflags & SPCFLAG_COPPER) + AMIGA.copper.cycle(); + + if (AMIGA.config.cpu.exact) { + regs.ipl = regs.ipl_pin; + if (regs.ipl > regs.intmask || regs.ipl == 7) + interrupt(regs.ipl); + } else { + if (AMIGA.spcflags & (SPCFLAG_INT | SPCFLAG_DOINT)) { + clr_special(SPCFLAG_INT | SPCFLAG_DOINT); + var intr = AMIGA.intlev(); + if (intr > 0 && intr > regs.intmask) + interrupt(intr); + } + } + + /*if ((AMIGA.spcflags & (SPCFLAG_BRK | SPCFLAG_MODE_CHANGE))) { + clr_special(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE); + clr_special(SPCFLAG_STOP); + regs.stopped = false; + return 1; + }*/ + if (AMIGA.state != CMD_CYCLE) { + if (regs.stopped) { + regs.stopped = false; + if (AMIGA.config.cpu.exact && AMIGA.config.cpu.model == 68000) + AMIGA.events.cycle(CYCLE_UNIT / 2 * 6); + clr_special(SPCFLAG_STOP); + } + return 1; + } + } + + if (AMIGA.spcflags & SPCFLAG_TRACE) + trace(); + + if (AMIGA.config.cpu.exact) { + if (regs.ipl > regs.intmask || regs.ipl == 7) + interrupt(regs.ipl); + } else { + if (AMIGA.spcflags & SPCFLAG_INT) { + clr_special(SPCFLAG_INT | SPCFLAG_DOINT); + var intr = AMIGA.intlev(); + if (intr > 0 && (intr > regs.intmask || intr == 7)) + interrupt(intr); + } + } + + if (AMIGA.spcflags & SPCFLAG_DOINT) { + clr_special(SPCFLAG_DOINT); + set_special(SPCFLAG_INT); + } + + /*if ((AMIGA.spcflags & (SPCFLAG_BRK | SPCFLAG_MODE_CHANGE))) { + clr_special(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE); + return 1; + }*/ + if (AMIGA.state != CMD_CYCLE) + return 1; + + return 0; + } + + this.cycles = 2; + + this.cycle = function() { + AMIGA.events.cycle(this.cycles * CYCLE_UNIT); + if (AMIGA.state != CMD_CYCLE) + return; + + this.cycles = cycle_real(); + + if (AMIGA.spcflags) { + if (cycle_spc(this.cycles * CYCLE_UNIT)) + return; + } + regs.ipl = regs.ipl_pin; + } + + this.setIPL = function(lev) { + regs.ipl_pin = lev; + } +} \ No newline at end of file diff --git a/sae/custom.js b/sae/custom.js new file mode 100644 index 0000000..18f96e1 --- /dev/null +++ b/sae/custom.js @@ -0,0 +1,350 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Custom() { + this.last_value = 0; + + this.setup = function() {} + this.reset = function() {} + + /*---------------------------------*/ + + this.load16_real = function (hpos, addr, noput) { + var writeonly = false; + var v; + + addr &= 0xfff; + + switch (addr & 0x1fe) { + case 0x002: v = AMIGA.DMACONR(hpos); break; + case 0x004: v = AMIGA.events.VPOSR(); break; + case 0x006: v = AMIGA.events.VHPOSR(); break; + + case 0x00A: v = AMIGA.input.JOY0DAT(); break; + case 0x00C: v = AMIGA.input.JOY1DAT(); break; + case 0x00E: v = AMIGA.playfield.sprites.CLXDAT(); break; + case 0x010: v = AMIGA.ADKCONR(); break; + + case 0x012: v = AMIGA.input.POT0DAT(); break; + case 0x014: v = AMIGA.input.POT1DAT(); break; + case 0x016: v = AMIGA.input.POTGOR(); break; + case 0x018: v = AMIGA.serial.SERDATR(); break; + case 0x01A: v = AMIGA.disk.DSKBYTR(hpos); break; + case 0x01C: v = AMIGA.INTENAR(); break; + case 0x01E: v = AMIGA.INTREQR(); break; + case 0x07C: { + var result = AMIGA.playfield.DENISEID(); + if (result[0]) + writeonly = true; + else + v = result[1]; + break; + } + + /*#ifdef AGA + case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A: + case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196: + case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2: + case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE: + case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA: + case 0x1BC: case 0x1BE: + if (AMIGA.config.chipset.type != SAEV_Config_Chipset_Type_AGA) + writeonly = true; + v = COLOR_READ ((addr & 0x3E) / 2); + break; + #endif*/ + + default: + writeonly = true; + } + + if (writeonly) { + v = this.last_value; + if (!noput) { + var old = this.last_value; + var l = 0xffff; + var r = this.store16_real(hpos, addr, l, 1); + if (r) { // register don't exist + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS) { + v = l; + } else { + if ((addr & 0x1fe) == 0) { + /*if (is_cycle_ce()) + v = this.last_value; + else*/ + v = l; + } + } + } else { + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS) + v = 0xffff; + else + v = l; + } + //BUG.info('Custom.load16_real() %08x read = %04x. value written = %04x', 0xdff000 | addr, v, l); + return v; + } + } + + this.last_value = v; + return v; + } + + this.load16_2 = function (addr) { + var hpos = AMIGA.events.hpos(); + + AMIGA.copper.sync_copper_with_cpu (hpos, 1); + return this.load16_real(hpos, addr, 0); + } + + this.load16 = function (addr) { + if (addr & 1) { + addr &= ~1; + return (this.load16_2(addr) << 8) | (this.load16_2(addr + 2) >> 8); + } + return this.load16_2(addr); + } + + this.load8 = function (addr) { + return this.load16_2(addr & ~1) >> ((addr & 1) ? 0 : 8); + } + + this.load32 = function (addr) { + return ((this.load16(addr) << 16) | this.load16(addr + 2)) >>> 0; + } + + /*---------------------------------*/ + + this.store16_real = function (hpos, addr, value, noget) { + if (!noget) this.last_value = value; + + addr &= 0x1fe; + value &= 0xffff; + + switch (addr) { + //case 0x00E: CLXDAT (); break; + + case 0x020: AMIGA.disk.DSKPTH(value); break; + case 0x022: AMIGA.disk.DSKPTL(value); break; + case 0x024: AMIGA.disk.DSKLEN(value, hpos); break; + case 0x026: /* AMIGA.disk.DSKDAT(value). Writing to DMA write registers won't do anything */; break; + + case 0x02A: AMIGA.events.VPOSW(value); break; + case 0x02C: AMIGA.events.VHPOSW(value); break; + case 0x02E: AMIGA.copper.COPCON(value); break; + case 0x030: AMIGA.serial.SERDAT(value); break; + case 0x032: AMIGA.serial.SERPER(value); break; + case 0x034: AMIGA.input.POTGO(value); break; + + case 0x040: AMIGA.blitter.BLTCON0(hpos, value); break; + case 0x042: AMIGA.blitter.BLTCON1(hpos, value); break; + + case 0x044: AMIGA.blitter.BLTAFWM(hpos, value); break; + case 0x046: AMIGA.blitter.BLTALWM(hpos, value); break; + + case 0x050: AMIGA.blitter.BLTAPTH(hpos, value); break; + case 0x052: AMIGA.blitter.BLTAPTL(hpos, value); break; + case 0x04C: AMIGA.blitter.BLTBPTH(hpos, value); break; + case 0x04E: AMIGA.blitter.BLTBPTL(hpos, value); break; + case 0x048: AMIGA.blitter.BLTCPTH(hpos, value); break; + case 0x04A: AMIGA.blitter.BLTCPTL(hpos, value); break; + case 0x054: AMIGA.blitter.BLTDPTH(hpos, value); break; + case 0x056: AMIGA.blitter.BLTDPTL(hpos, value); break; + + case 0x058: AMIGA.blitter.BLTSIZE(hpos, value); break; + + case 0x064: AMIGA.blitter.BLTAMOD(hpos, value); break; + case 0x062: AMIGA.blitter.BLTBMOD(hpos, value); break; + case 0x060: AMIGA.blitter.BLTCMOD(hpos, value); break; + case 0x066: AMIGA.blitter.BLTDMOD(hpos, value); break; + + case 0x070: AMIGA.blitter.BLTCDAT(hpos, value); break; + case 0x072: AMIGA.blitter.BLTBDAT(hpos, value); break; + case 0x074: AMIGA.blitter.BLTADAT(hpos, value); break; + + case 0x07E: AMIGA.disk.DSKSYNC(value, hpos); break; + + case 0x080: AMIGA.copper.COP1LCH(value); break; + case 0x082: AMIGA.copper.COP1LCL(value); break; + case 0x084: AMIGA.copper.COP2LCH(value); break; + case 0x086: AMIGA.copper.COP2LCL(value); break; + + case 0x088: AMIGA.copper.COPJMP(1, 0); break; + case 0x08A: AMIGA.copper.COPJMP(2, 0); break; + + case 0x08E: AMIGA.playfield.DIWSTRT(value); break; + case 0x090: AMIGA.playfield.DIWSTOP(value); break; + case 0x092: AMIGA.playfield.DDFSTRT(value); break; + case 0x094: AMIGA.playfield.DDFSTOP(value); break; + + case 0x096: AMIGA.DMACON(value, hpos); break; + case 0x098: AMIGA.playfield.sprites.CLXCON(value); break; + case 0x09A: AMIGA.INTENA(value); break; + case 0x09C: AMIGA.INTREQ(value); break; + case 0x09E: AMIGA.ADKCON(value, hpos); break; + + case 0x0A0: AMIGA.audio.AUDxLCH(0, value); break; + case 0x0A2: AMIGA.audio.AUDxLCL(0, value); break; + case 0x0A4: AMIGA.audio.AUDxLEN(0, value); break; + case 0x0A6: AMIGA.audio.AUDxPER(0, value); break; + case 0x0A8: AMIGA.audio.AUDxVOL(0, value); break; + case 0x0AA: AMIGA.audio.AUDxDAT(0, value); break; + + case 0x0B0: AMIGA.audio.AUDxLCH(1, value); break; + case 0x0B2: AMIGA.audio.AUDxLCL(1, value); break; + case 0x0B4: AMIGA.audio.AUDxLEN(1, value); break; + case 0x0B6: AMIGA.audio.AUDxPER(1, value); break; + case 0x0B8: AMIGA.audio.AUDxVOL(1, value); break; + case 0x0BA: AMIGA.audio.AUDxDAT(1, value); break; + + case 0x0C0: AMIGA.audio.AUDxLCH(2, value); break; + case 0x0C2: AMIGA.audio.AUDxLCL(2, value); break; + case 0x0C4: AMIGA.audio.AUDxLEN(2, value); break; + case 0x0C6: AMIGA.audio.AUDxPER(2, value); break; + case 0x0C8: AMIGA.audio.AUDxVOL(2, value); break; + case 0x0CA: AMIGA.audio.AUDxDAT(2, value); break; + + case 0x0D0: AMIGA.audio.AUDxLCH(3, value); break; + case 0x0D2: AMIGA.audio.AUDxLCL(3, value); break; + case 0x0D4: AMIGA.audio.AUDxLEN(3, value); break; + case 0x0D6: AMIGA.audio.AUDxPER(3, value); break; + case 0x0D8: AMIGA.audio.AUDxVOL(3, value); break; + case 0x0DA: AMIGA.audio.AUDxDAT(3, value); break; + + case 0x0E0: AMIGA.playfield.BPLxPTx(value, 0, 1); break; + case 0x0E2: AMIGA.playfield.BPLxPTx(value, 0, 0); break; + case 0x0E4: AMIGA.playfield.BPLxPTx(value, 1, 1); break; + case 0x0E6: AMIGA.playfield.BPLxPTx(value, 1, 0); break; + case 0x0E8: AMIGA.playfield.BPLxPTx(value, 2, 1); break; + case 0x0EA: AMIGA.playfield.BPLxPTx(value, 2, 0); break; + case 0x0EC: AMIGA.playfield.BPLxPTx(value, 3, 1); break; + case 0x0EE: AMIGA.playfield.BPLxPTx(value, 3, 0); break; + case 0x0F0: AMIGA.playfield.BPLxPTx(value, 4, 1); break; + case 0x0F2: AMIGA.playfield.BPLxPTx(value, 4, 0); break; + case 0x0F4: AMIGA.playfield.BPLxPTx(value, 5, 1); break; + case 0x0F6: AMIGA.playfield.BPLxPTx(value, 5, 0); break; + /*case 0x0F8: AMIGA.playfield.BPLxPTx(value, 6, 1); break; + case 0x0FA: AMIGA.playfield.BPLxPTx(value, 6, 0); break; + case 0x0FC: AMIGA.playfield.BPLxPTx(value, 7, 1); break; + case 0x0FE: AMIGA.playfield.BPLxPTx(value, 7, 0); break;*/ + + case 0x100: AMIGA.playfield.BPLCON0(value); break; + case 0x102: AMIGA.playfield.BPLCON1(value); break; + case 0x104: AMIGA.playfield.BPLCON2(value); break; + //case 0x106: AMIGA.playfield.BPLCON3(value); break; //ECS_DENISE + + case 0x108: AMIGA.playfield.BPL1MOD(value); break; + case 0x10A: AMIGA.playfield.BPL2MOD(value); break; + //case 0x10E: AMIGA.playfield.sprites.CLXCON2 (value); break; //AGA + + case 0x110: AMIGA.playfield.BPLxDAT(value, 0); break; + case 0x112: AMIGA.playfield.BPLxDAT(value, 1); break; + case 0x114: AMIGA.playfield.BPLxDAT(value, 2); break; + case 0x116: AMIGA.playfield.BPLxDAT(value, 3); break; + case 0x118: AMIGA.playfield.BPLxDAT(value, 4); break; + case 0x11A: AMIGA.playfield.BPLxDAT(value, 5); break; + //case 0x11C: AMIGA.playfield.BPLxDAT(value, 6); break; + //case 0x11E: AMIGA.playfield.BPLxDAT(value, 7); break; + + case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A: + case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196: + case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2: + case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE: + case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA: + case 0x1BC: case 0x1BE: + AMIGA.playfield.COLORxx(value & 0xfff, (addr & 0x3E) >> 1); + break; + case 0x120: case 0x124: case 0x128: case 0x12C: + case 0x130: case 0x134: case 0x138: case 0x13C: + AMIGA.playfield.sprites.SPRxPTH(value, (addr - 0x120) >> 2); + break; + case 0x122: case 0x126: case 0x12A: case 0x12E: + case 0x132: case 0x136: case 0x13A: case 0x13E: + AMIGA.playfield.sprites.SPRxPTL(value, (addr - 0x122) >> 2); + break; + case 0x140: case 0x148: case 0x150: case 0x158: + case 0x160: case 0x168: case 0x170: case 0x178: + AMIGA.playfield.sprites.SPRxPOS(value, (addr - 0x140) >> 3); + break; + case 0x142: case 0x14A: case 0x152: case 0x15A: + case 0x162: case 0x16A: case 0x172: case 0x17A: + AMIGA.playfield.sprites.SPRxCTL(value, (addr - 0x142) >> 3); + break; + case 0x144: case 0x14C: case 0x154: case 0x15C: + case 0x164: case 0x16C: case 0x174: case 0x17C: + AMIGA.playfield.sprites.SPRxDATA(value, (addr - 0x144) >> 3); + break; + case 0x146: case 0x14E: case 0x156: case 0x15E: + case 0x166: case 0x16E: case 0x176: case 0x17E: + AMIGA.playfield.sprites.SPRxDATB(value, (addr - 0x146) >> 3); + break; + + case 0x36: AMIGA.input.JOYTEST(value); break; + case 0x5A: AMIGA.blitter.BLTCON0L(hpos, value); break; //ECS + case 0x5C: AMIGA.blitter.BLTSIZV(hpos, value); break; //ECS + case 0x5E: AMIGA.blitter.BLTSIZH(hpos, value); break; //ECS + //case 0x1E4: AMIGA.playfield.DIWHIGH(value); break; //ECS + //case 0x10C: AMIGA.playfield.BPLCON4(value); break; //AGA + + //#ifndef CUSTOM_SIMPLE + //case 0x1DC: AMIGA.events.BEAMCON0(value); break; + /*#ifdef ECS_DENISE + case 0x1C0: if (htotal != value) { htotal = value; varsync (); } break; + case 0x1C2: if (hsstop != value) { hsstop = value; varsync (); } break; + case 0x1C4: if (hbstrt != value) { hbstrt = value; varsync (); } break; + case 0x1C6: if (hbstop != value) { hbstop = value; varsync (); } break; + case 0x1C8: if (vtotal != value) { vtotal = value; varsync (); } break; + case 0x1CA: if (vsstop != value) { vsstop = value; varsync (); } break; + case 0x1CC: if (vbstrt < value || vbstrt > value + 1) { vbstrt = value; varsync (); } break; + case 0x1CE: if (vbstop < value || vbstop > value + 1) { vbstop = value; varsync (); } break; + case 0x1DE: if (hsstrt != value) { hsstrt = value; varsync (); } break; + case 0x1E0: if (vsstrt != value) { vsstrt = value; varsync (); } break; + case 0x1E2: if (hcenter != value) { hcenter = value; varsync (); } break; + endif*/ + //endif + + //case 0x1FC: FMODE (hpos, value); break; //AGA + //case 0x1FE: FNULL (value); break; + case 0x1FE: break; + + /* writing to read-only register causes read access */ + default: { + if (!noget) { + //BUG.info('Custom.store16_real() %04x written', addr); + this.load16_real(hpos, addr, 1); + } + return true; + } + } + return false; + } + + this.store16 = function (addr, value) { + var hpos = AMIGA.events.hpos(); + AMIGA.copper.sync_copper_with_cpu(hpos, 1); + if (addr & 1) { + addr &= ~1; + this.store16_real(hpos, addr, (value >> 8) | (value & 0xff00), 0); + this.store16_real(hpos, addr + 2, (value << 8) | (value & 0x00ff), 0); + return; + } + this.store16_real(hpos, addr, value, 0); + } + + this.store8 = function (addr, value) { + this.store16(addr & ~1, ((value << 8) | (value & 0xff)) & 0xffff); + } + + this.store32 = function (addr, value) { + this.store16(addr & 0xfffe, value >>> 16); + this.store16((addr + 2) & 0xfffe, value & 0xffff); + } + +} diff --git a/sae/disk.js b/sae/disk.js new file mode 100644 index 0000000..3ec4c3b --- /dev/null +++ b/sae/disk.js @@ -0,0 +1,1674 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +const LONGWRITEMODE = 0; + +const FLOPPY_DRIVE_HD = 1; +const FLOPPY_WRITE_MAXLEN = 0x3800; + +const DDHDMULT = FLOPPY_DRIVE_HD ? 2 : 1; + +const MAX_FLOPPY_DRIVES = 4; +const MAX_SECTORS = DDHDMULT * 11; +const MAX_TRACKS = 2 * 83; + +const MIN_STEPLIMIT_CYCLE = CYCLE_UNIT * 250; + +const DISK_INDEXSYNC = 1; +const DISK_WORDSYNC = 2; +const DISK_REVOLUTION = 4; /* 8,16,32,64 */ + +const DSKREADY_UP_TIME = 20; +const DSKREADY_DOWN_TIME = 50; +const WORDSYNC_TIME = 11; + +const DSKDMA_OFF = 0; +const DSKDMA_INIT = 1; +const DSKDMA_READ = 2; +const DSKDMA_WRITE = 3; + +const DRIVE_ID_NONE = 0x00000000; +const DRIVE_ID_35DD = 0xFFFFFFFF; +const DRIVE_ID_35HD = 0xAAAAAAAA; +const DRIVE_ID_525SD = 0x55555555; + +const TRACK_AMIGADOS = 0; +const TRACK_RAW = 1; +const TRACK_RAW1 = 2; +const TRACK_PCDOS = 3; +const TRACK_DISKSPARE= 4; +const TRACK_NONE = 5; + +const ADF_NONE = -1; +const ADF_NORMAL = 0; +const ADF_EXT1 = 1; +const ADF_EXT2 = 2; +const ADF_FDI = 3; +const ADF_IPF = 4; +const ADF_PCDOS = 5; + +function trackid() { + var len = 0; + var offs = 0; + var bitlen = 0; + var track = 0; + var sync = 0; + var type = TRACK_NONE; + var revolutions = 0; +} + +function get_floppy_speed() { + var speed = AMIGA.config.floppy.speed == SAEV_Config_Floppy_Speed_Turbo ? 100 : AMIGA.config.floppy.speed; + return Math.floor((AMIGA.config.video.ntsc ? 1812 : 1829) * 100 / speed); +} + +function uaerand() { + var l = 0, u = 0xffffffff; + return Math.floor((Math.random() * (u - l + 1)) + l); +} + +function Drive(number) { + this.num = number; + this.diskdata = null; + this.diskfile = null; + //this.writediskfile = null; + this.filetype = 0; //drive_filetype + this.trackdata = new Array(MAX_TRACKS); for (var i = 0; i < MAX_TRACKS; i++) this.trackdata[i] = new trackid(); + //this.writetrackdata = new Array(MAX_TRACKS); for (var i = 0; i < MAX_TRACKS; i++) this.trackdata[i] = new trackid(); + this.writebuffer = new Uint8Array(544 * MAX_SECTORS); for (var i = 0; i < 544 * MAX_SECTORS; i++) this.writebuffer[i] = 0; + this.buffered_cyl = 0; + this.buffered_side = 0; + this.cyl = 0; + this.motoroff = true; + this.motordelay = false; /* dskrdy needs some clock cycles before it changes after switching off motor */ + this.state = 0; + this.wrprot = false; + this.bigmfmbuf = new Uint16Array(0x4000 * DDHDMULT); for (var i = 0; i < 0x4000 * DDHDMULT; i++) this.bigmfmbuf[i] = 0; + this.tracktiming = new Uint16Array(0x4000 * DDHDMULT); for (var i = 0; i < 0x4000 * DDHDMULT; i++) this.tracktiming[i] = 0; + this.multi_revolution = 0; + this.skipoffset = 0; + this.mfmpos = 0; + this.indexoffset = 0; + this.tracklen = 0; + this.revolutions = 0; + this.prevtracklen = 0; + this.trackspeed = 0; + this.num_tracks = 0; + this.write_num_tracks = 0; + this.num_secs = 0; + this.hard_num_cyls = 0; + this.dskchange = false; + this.dskchange_time = 0; + this.dskready = false; + this.dskready_up_time = 0; + this.dskready_down_time = 0; + this.writtento = 0; + this.steplimit = 0; + this.steplimitcycle = 0; + this.indexhack = 0; + this.indexhackmode = 0; + this.ddhd = 0; + this.idbit = 0; + this.drive_id_scnt = 0; + this.drive_id = DRIVE_ID_NONE; + this.useturbo = false; + this.floppybitcounter = 0; + + this.id_name = function() { + switch (this.drive_id) { + case DRIVE_ID_35HD : return '3.5HD'; + case DRIVE_ID_525SD: return '5.25SD'; + case DRIVE_ID_35DD : return '3.5DD'; + case DRIVE_ID_NONE : return 'NONE'; + } + return 'UNKNOWN'; + } + + this.set_id = function() { + switch (AMIGA.config.floppy.drive[this.num].type) { + case SAEV_Config_Floppy_Type_35_HD: { + if (FLOPPY_DRIVE_HD) { + if (!this.diskfile || this.ddhd <= 1) + this.drive_id = DRIVE_ID_35DD; + else + this.drive_id = DRIVE_ID_35HD; + } else + this.drive_id = DRIVE_ID_35DD; + + break; + } + case SAEV_Config_Floppy_Type_35_DD: + this.drive_id = DRIVE_ID_35DD; + break; + case SAEV_Config_Floppy_Type_525_SD: + this.drive_id = DRIVE_ID_525SD; + break; + case SAEV_Config_Floppy_Type_None: + this.drive_id = DRIVE_ID_NONE; + break; + default: + this.drive_id = DRIVE_ID_35DD; + } + //BUG.info('Drive.set_id() DF%d set to %s', this.num, this.id_name()); + } + + this.get_floppy_speed2 = function() { + var m = Math.floor(get_floppy_speed() * this.tracklen / (2 * 8 * (AMIGA.config.video.ntsc ? 6399 : 6334) * this.ddhd)); + if (m <= 0) m = 1; + return m; + } + + this.reset = function() { + //BUG.info('Drive.reset() DF%d', this.num); + this.filetype = ADF_NONE; + this.diskfile = null; + //this.writediskfile = null; + this.motoroff = true; + this.idbit = 0; + this.drive_id = 0; + this.drive_id_scnt = 0; + this.indexhackmode = 0; + this.dskchange_time = 0; + this.dskchange = false; + this.dskready_down_time = 0; + this.dskready_up_time = 0; + this.buffered_cyl = -1; + this.buffered_side = -1; + if (this.num == 0 && AMIGA.config.floppy.drive[this.num].type == SAEV_Config_Floppy_Type_35_DD) + this.indexhackmode = 1; + this.set_id(); + } + + this.updatemfmpos = function() { + if (this.prevtracklen) + this.mfmpos = this.mfmpos * Math.floor(Math.floor(this.tracklen * 1000 / this.prevtracklen) / 1000); + this.mfmpos %= this.tracklen; + this.prevtracklen = this.tracklen; + } + + this.reset_track = function() { + //BUG.info('Drive.reset_track() DF%d', this.num); + this.tracklen = (AMIGA.config.video.ntsc ? 6399 : 6334) * this.ddhd * 2 * 8; + this.revolutions = 1; + this.trackspeed = get_floppy_speed(); + this.buffered_side = -1; + this.skipoffset = -1; + this.tracktiming[0] = 0; + for (var i = 0; i < (AMIGA.config.video.ntsc ? 6399 : 6334) * this.ddhd; i++) this.bigmfmbuf[i] = 0xaaaa; //memset (this.bigmfmbuf, 0xaa, (AMIGA.config.video.ntsc ? 6399 : 6334) * 2 * this.ddhd); + this.updatemfmpos(); + } + + function strncmp_as(str1, str2, n) { + for (var i = 0; i < n; i++) { + if (str1[i] != (str2.charCodeAt(i) & 0xff)) + return 1; + } + return 0; + } + function strncmp_aa(str1, str2, n) { + for (var i = 0; i < n; i++) { + if (str1[i] != str2[i]) + return 1; + } + return 0; + } + this.insert = function() { + //BUG.info('DF%d.insert()', this.num); + //const exeheader = [0x00,0x00,0x03,0xf3,0x00,0x00,0x00,0x00]; + + this.filetype = ADF_NONE; + this.diskfile = null; + //this.writediskfile = null; + this.ddhd = 1; + this.num_secs = 0; + this.hard_num_cyls = AMIGA.config.floppy.drive[this.num].type == SAEV_Config_Floppy_Type_525_SD ? 40 : 80; + this.tracktiming[0] = 0; + this.useturbo = false; + this.indexoffset = 0; + + var size = 0; + if (this.diskdata !== null) { + this.diskfile = new Uint8Array(this.diskdata.length); + for (var i = 0; i < this.diskdata.length; i++) + this.diskfile[i] = this.diskdata[i]; + size = this.diskfile.length; + } + + if (!this.motoroff) { + this.dskready_up_time = DSKREADY_UP_TIME; + this.dskready_down_time = 0; + } + if (this.diskfile === null) { + this.reset_track(); + return 0; + } + + if (strncmp_as(this.diskfile, 'UAE-1ADF', 8) == 0) { + //BUG.info('DF%d.insert() UAE-1ADF', this.num); + + //read_header_ext2 (drv->diskfile, drv->trackdata, &drv->num_tracks, &drv->ddhd); + this.filetype = ADF_EXT2; + this.num_secs = 11; + if (this.ddhd > 1) + this.num_secs = 22; + } + else if (strncmp_as(this.diskfile, 'UAE--ADF', 8) == 0) { + //BUG.info('DF%d.insert() UAE--ADF', this.num); + var offs = 160 * 4 + 8; + + this.wrprot = true; + this.filetype = ADF_EXT1; + this.num_tracks = 160; + this.num_secs = 11; + + for (var i = 0; i < this.num_tracks; i++) { + var buffer = []; + for (var j = 0; j < 4; j++) + buffer[j] = this.diskfile[8 + i * 4 + j]; + + this.trackdata[i].sync = buffer[0] * 256 + buffer[1]; + this.trackdata[i].len = buffer[2] * 256 + buffer[3]; + this.trackdata[i].offs = offs; + this.trackdata[i].revolutions = 1; + + if (this.trackdata[i].sync == 0) { + this.trackdata[i].type = TRACK_AMIGADOS; + this.trackdata[i].bitlen = 0; + } else { + this.trackdata[i].type = TRACK_RAW1; + this.trackdata[i].bitlen = this.trackdata[i].len * 8; + } + offs += this.trackdata[i].len; + } + } + /*else if (strncmp_aa(this.diskfile, exeheader, 8) == 0) { + //BUG.info('DF%d.insert() EXE', this.num); + //struct zfile *z = zfile_fopen_empty(NULL, "", 512 * 1760); + //createimagefromexe (drv->diskfile, z); + //zfile_fclose (drv->diskfile); + + //this.diskfile = z; + this.filetype = ADF_NORMAL; + this.num_tracks = 160; + this.num_secs = 11; + + for (var i = 0; i < this.num_tracks; i++) { + this.trackdata[i].type = TRACK_AMIGADOS; + this.trackdata[i].len = 512 * this.num_secs; + this.trackdata[i].bitlen = 0; + this.trackdata[i].offs = i * 512 * this.num_secs; + this.trackdata[i].revolutions = 1; + } + this.useturbo = true; + }*/ + else { + this.filetype = ADF_NORMAL; + + /* high-density or diskspare disk? */ + var ds = false; + this.num_tracks = 0; + if (size > 160 * 11 * 512 + 511) { /* larger than standard adf? */ + for (var i = 80; i <= 83; i++) { + if (size == i * 22 * 512 * 2) { // HD + this.ddhd = 2; + this.num_tracks = Math.floor(size / (512 * (this.num_secs = 22))); + break; + } + if (size == i * 11 * 512 * 2) { // >80 cyl DD + this.num_tracks = Math.floor(size / (512 * (this.num_secs = 11))); + break; + } + if (size == i * 12 * 512 * 2) { // ds 12 sectors + this.num_tracks = Math.floor(size / (512 * (this.num_secs = 12))); + ds = true; + break; + } + if (size == i * 24 * 512 * 2) { // ds 24 sectors + this.num_tracks = Math.floor(size / (512 * (this.num_secs = 24))); + this.ddhd = 2; + ds = true; + break; + } + } + if (this.num_tracks == 0) { + this.num_tracks = Math.floor(size / (512 * (this.num_secs = 22))); + this.ddhd = 2; + } + } else + this.num_tracks = Math.floor(size / (512 * (this.num_secs = 11))); + + if (!ds && this.num_tracks > MAX_TRACKS) + Fatal(SAEE_Disk_File_Too_Big, sprintf('The diskfile in DF%d is too big. (%d tracks)', this.num, this.num_tracks)); + + for (var i = 0; i < this.num_tracks; i++) { + this.trackdata[i].type = ds ? TRACK_DISKSPARE : TRACK_AMIGADOS; + this.trackdata[i].len = 512 * this.num_secs; + this.trackdata[i].bitlen = 0; + this.trackdata[i].offs = i * 512 * this.num_secs; + this.trackdata[i].revolutions = 1; + } + } + this.set_id(); + this.fill_bigbuf(AMIGA.disk.side, 1); + + this.mfmpos = uaerand(); + this.mfmpos |= (uaerand() << 16); + this.mfmpos %= this.tracklen; + this.prevtracklen = 0; + return 1; + } + + this.eject = function() { + //BUG.info('DF%d.eject()', this.num); + this.filetype = ADF_NONE; + this.diskfile = null; + //this.writediskfile = null; + this.dskchange = true; + this.dskchange_time = 0; + this.dskready = false; + this.dskready_up_time = 0; + this.dskready_down_time = 0; + this.ddhd = 1; + this.set_id(); + } + + this.is_empty = function() { + return this.diskfile === null; + } + + this.set_steplimit = function() { + this.steplimit = 10; + this.steplimitcycle = AMIGA.events.currcycle; + } + + this.step = function() { + if (!this.is_empty()) + this.dskchange = 0; + + if (this.steplimit && AMIGA.events.currcycle - this.steplimitcycle < MIN_STEPLIMIT_CYCLE) { + BUG.info('Drive.step() DF%d, ignoring step %d', this.num, Math.floor((AMIGA.events.currcycle - this.steplimitcycle) * CYCLE_UNIT_INV)); + return; + } + + this.set_steplimit(); + + if (AMIGA.disk.direction) { + if (this.cyl) + this.cyl--; + //else BUG.info('Drive.step() DF%d, program tried to step beyond track zero', this.num); //'no-click' programs does that + } else { + var maxtrack = this.hard_num_cyls; + if (this.cyl < maxtrack + 3) + this.cyl++; + //if (this.cyl >= maxtrack) BUG.info('Drive.step() DF%d, program tried to step over track %d', this.num, maxtrack); //'no-click' programs does that + } + AMIGA.disk.rand_shifter(); + AMIGA.config.hooks.floppy_step(this.num, this.cyl); + } + + this.is_track0 = function() { + return this.cyl == 0; + } + + this.is_writeprotected = function() { + return this.wrprot || this.diskfile === null; + } + + this.is_running = function() { + return !this.motoroff; + } + + this.set_motor = function(off) { + if (this.motoroff && !off) { + this.dskready_up_time = DSKREADY_UP_TIME; + AMIGA.disk.rand_shifter(); + } + if (!this.motoroff && off) { + this.drive_id_scnt = 0; /* Reset id shift reg counter */ + this.dskready_down_time = DSKREADY_DOWN_TIME; + + if (AMIGA.config.cpu.model <= 68010 && AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Original) { + this.motordelay = true; + AMIGA.events.event2_newevent2(30, this.num, function(v) { AMIGA.disk.motordelay_func(v); }); + } + } + this.motoroff = off; + if (this.motoroff) { + this.dskready = false; + this.dskready_up_time = 0; + } else { + this.dskready_down_time = 0; + } + } + + /* get one bit from MFM bit stream */ + this.getonebit = function(mfmpos) { + return (this.bigmfmbuf[mfmpos >> 4] & (1 << (15 - (mfmpos & 15)))) ? 1 : 0; + } + this.decode_amigados = function() { + var gap_len = AMIGA.config.video.ntsc ? 415 : 350; + var tr = this.cyl * 2 + AMIGA.disk.side; + var len = this.num_secs * 544 + gap_len; + var bigmfmpos = gap_len; + var sec; + var i; + + for (i = 0; i < len; i++) + this.bigmfmbuf[i] = 0xaaaa; + + this.skipoffset = Math.floor((gap_len * 8) / 3) * 2; + this.tracklen = len * 2 * 8; + + for (sec = 0; sec < this.num_secs; sec++) { + var secbuf = new Uint8Array(544); + var mfmbuf = new Uint16Array(544); + var deven, dodd; + var hck = 0, dck = 0; + + secbuf[0] = secbuf[1] = 0x00; + secbuf[2] = secbuf[3] = 0xa1; + secbuf[4] = 0xff; + secbuf[5] = tr; + secbuf[6] = sec; + secbuf[7] = this.num_secs - sec; + + for (i = 8; i < 24; i++) + secbuf[i] = 0; + + //read_floppy_data (this.diskfile, ti, sec * 512, &secbuf[32], 512); + { + var offset = this.trackdata[tr].offs + sec * 512; + for (i = 0; i < 512; i++) secbuf[32 + i] = this.diskfile[offset + i]; + } + + mfmbuf[0] = mfmbuf[1] = 0xaaaa; + mfmbuf[2] = mfmbuf[3] = 0x4489; + + deven = ((secbuf[4] << 24) | (secbuf[5] << 16) | (secbuf[6] << 8) | (secbuf[7])) >>> 0; + dodd = deven >>> 1; + deven &= 0x55555555; + dodd &= 0x55555555; + + mfmbuf[4] = dodd >>> 16; + mfmbuf[5] = dodd & 0xffff; + mfmbuf[6] = deven >>> 16; + mfmbuf[7] = deven & 0xffff; + + for (i = 8; i < 48; i++) + mfmbuf[i] = 0xaaaa; + for (i = 0; i < 512; i += 4) { + deven = ((secbuf[i + 32] << 24) | (secbuf[i + 33] << 16) | (secbuf[i + 34] << 8) | (secbuf[i + 35])) >>> 0; + dodd = deven >>> 1; + deven &= 0x55555555; + dodd &= 0x55555555; + mfmbuf[(i >> 1) + 32] = dodd >>> 16; + mfmbuf[(i >> 1) + 33] = dodd & 0xffff; + mfmbuf[(i >> 1) + 256 + 32] = deven >>> 16; + mfmbuf[(i >> 1) + 256 + 33] = deven & 0xffff; + } + + for (i = 4; i < 24; i += 2) + hck ^= ((mfmbuf[i] << 16) | mfmbuf[i + 1]) >>> 0; + + deven = dodd = hck; + dodd >>>= 1; + mfmbuf[24] = dodd >>> 16; + mfmbuf[25] = dodd & 0xffff; + mfmbuf[26] = deven >>> 16; + mfmbuf[27] = deven & 0xffff; + + for (i = 32; i < 544; i += 2) + dck ^= ((mfmbuf[i] << 16) | mfmbuf[i + 1]) >>> 0; + + deven = dodd = dck; + dodd >>>= 1; + mfmbuf[28] = dodd >>> 16; + mfmbuf[29] = dodd & 0xffff; + mfmbuf[30] = deven >>> 16; + mfmbuf[31] = deven & 0xffff; + + //mfmcode (mfmbuf + 4, 544 - 4); static this.mfmcode (var * mfm, var words) + { + var words = 540, lastword = 0, pos = 4; + while (words--) { + //var v = *mfm; + var v = mfmbuf[pos]; + var lv = ((lastword << 16) | v) >>> 0; + var nlv = (0x55555555 & ~lv) >>> 0; + var mfmbits = (((nlv << 1) & (nlv >>> 1)) >>> 0) & 0xffff; + //*mfm++ = v | mfmbits; + mfmbuf[pos] = v | mfmbits; + lastword = v; + pos++; + } + } + + for (i = 0; i < 544; i++) { + this.bigmfmbuf[bigmfmpos % len] = mfmbuf[i]; bigmfmpos++; + } + } + } + + this.decode_raw = function() { + var tr = this.cyl * 2 + AMIGA.disk.side; + + var base_offset = this.trackdata[tr].type == TRACK_RAW ? 0 : 1; + this.tracklen = this.trackdata[tr].bitlen + 16 * base_offset; + this.bigmfmbuf[0] = this.trackdata[tr].sync; + var len = Math.floor((this.trackdata[tr].bitlen + 7) / 8); + var buf = new Uint8Array(len); + + //read_floppy_data (this.diskfile, ti, 0, (var*)(this.bigmfmbuf + base_offset), Math.floor((ti->bitlen + 7) / 8)); + { + var offset = this.trackdata[tr].offs; + for (var i = 0; i < len; i++) + buf[i] = this.diskfile[offset + i]; + } + + for (var i = base_offset; i < Math.floor((this.tracklen + 15) / 16); i++) + this.bigmfmbuf[i] = 256 * buf[(i - base_offset) << 1] + buf[((i - base_offset) << 1) + 1]; + + //BUG.info('DF%d.decode_raw() rawtrack %d, offset %d', this.num, tr, this.trackdata[tr].offs); + } + + this.fill_bigbuf = function(force) { + var tr = this.cyl * 2 + AMIGA.disk.side; + + if (!this.diskfile || tr >= this.num_tracks) { + this.reset_track(); + return; + } + if (!force && this.buffered_cyl == this.cyl && this.buffered_side == AMIGA.disk.side) + return; + + this.indexoffset = 0; + this.multi_revolution = 0; + this.tracktiming[0] = 0; + this.skipoffset = -1; + this.revolutions = 1; + + /*if (this.writediskfile && this.writetrackdata[tr].bitlen > 0) { + var i; + trackid *wti = &this.writetrackdata[tr]; + this.tracklen = wti->bitlen; + this.revolutions = wti->revolutions; + read_floppy_data (this.writediskfile, wti, 0, (var*)this.bigmfmbuf, Math.floor((wti->bitlen + 7) / 8)); + for (i = 0; i < Math.floor((this.tracklen + 15) / 16); i++) { + var *mfm = this.bigmfmbuf + i; + var *data = (var *) mfm; + *mfm = 256 * *data + *(data + 1); + } + write_log ('track %d, length %d read from \'saveimage\'\n', tr, this.tracklen); + } else*/ + if (this.trackdata[tr].type == TRACK_NONE) { + } + else if (this.trackdata[tr].type == TRACK_AMIGADOS) + this.decode_amigados(); + else if (this.trackdata[tr].type == TRACK_DISKSPARE) + this.decode_diskspare(); + else if (this.trackdata[tr].type == TRACK_PCDOS) + this.decode_pcdos(); + else + this.decode_raw() + + this.buffered_side = AMIGA.disk.side; + this.buffered_cyl = this.cyl; + if (this.tracklen == 0) { + this.tracklen = (AMIGA.config.video.ntsc ? 6399 : 6334) * this.ddhd * 2 * 8; + for (var i = 0; i < (AMIGA.config.video.ntsc ? 6399 : 6334) * this.ddhd; i++) this.bigmfmbuf[i] = 0; //memset (this.bigmfmbuf, 0, (AMIGA.config.video.ntsc ? 6399 : 6334) * 2 * this.ddhd); + } + + this.trackspeed = this.get_floppy_speed2(); + this.updatemfmpos(); + } + + this.getmfmword = function(mbuf, shift) { + return (((this.bigmfmbuf[mbuf] << shift) | (this.bigmfmbuf[mbuf + 1] >>> (16 - shift))) >>> 0) & 0xffff; + } + this.getmfmlong = function(mbuf, shift) { + return (((this.getmfmword(mbuf, shift) << 16) | this.getmfmword(mbuf + 1, shift)) >>> 0) & 0x55555555; + } + this.decode_buffer = function(checkmode) { + var mbuf = 0; + var cyl = this.cyl; + var drvsec = this.num_secs; + var ddhd = this.ddhd; + var filetype = this.filetype; + + var i, secwritten = 0; + var fwlen = (AMIGA.config.video.ntsc ? 6399 : 6334) * ddhd; + var length = 2 * fwlen; + var odd, even, chksum, id, dlong; + var secbuf = new Uint8Array(544); + var sectable = new Array(22); + var mend = length - (4 + 16 + 8 + 512); + var shift = 0; + + for (i = 0; i < sectable.length; i++) sectable[i] = 0; //memset (sectable, 0, sizeof (sectable)); + for (i = 0; i < fwlen; i++) this.bigmfmbuf[fwlen + i] = this.bigmfmbuf[i]; //memcpy (mbuf + fwlen, mbuf, fwlen * sizeof(uae_u16)); + + while (secwritten < drvsec) { + while (this.getmfmword(mbuf, shift) != 0x4489) { + if (mbuf >= mend) return 1; + shift++; + if (shift == 16) { + shift = 0; + mbuf++; + } + } + while (this.getmfmword(mbuf, shift) == 0x4489) { + if (mbuf >= mend) return 10; + mbuf++; + } + + odd = this.getmfmlong(mbuf, shift); + even = this.getmfmlong(mbuf + 2, shift); + mbuf += 4; + id = (((odd << 1) | even) >>> 0) & 0xffffffff; + + var trackoffs = (id & 0xff00) >>> 8; + if (trackoffs + 1 > drvsec) { + BUG.info('DF%d.decode_buffer() weird sector number %d', this.num, trackoffs); + if (filetype == ADF_EXT2) return 2; + continue; + } + chksum = (odd ^ even) >>> 0; + for (i = 0; i < 4; i++) { + odd = this.getmfmlong(mbuf, shift); + even = this.getmfmlong(mbuf + 8, shift); + mbuf += 2; + + dlong = (((odd << 1) | even) >>> 0) & 0xffffffff; + if (dlong && !checkmode) { + if (filetype == ADF_EXT2) return 6; + secwritten = -200; + } + chksum ^= odd ^ even; + chksum &= 0xffffffff; + } + mbuf += 8; + odd = this.getmfmlong(mbuf, shift); + even = this.getmfmlong(mbuf + 2, shift); + mbuf += 4; + if (((((odd << 1) | even) >>> 0) & 0xffffffff) != chksum || ((id & 0x00ff0000) >> 16) != cyl * 2 + AMIGA.disk.side) { + BUG.info('DF%d.decode_buffer() checksum error on sector %d header', this.num, trackoffs); + if (filetype == ADF_EXT2) return 3; + continue; + } + odd = this.getmfmlong(mbuf, shift); + even = this.getmfmlong(mbuf + 2, shift); + mbuf += 4; + chksum = (((odd << 1) | even) >>> 0) & 0xffffffff; + for (i = 0; i < 512; i += 4) { + odd = this.getmfmlong(mbuf, shift); + even = this.getmfmlong(mbuf + 256, shift); + mbuf += 2; + dlong = (((odd << 1) | even) >>> 0) & 0xffffffff; + secbuf[32 + i] = (dlong >>> 24) & 0xff; + secbuf[33 + i] = (dlong >>> 16) & 0xff; + secbuf[34 + i] = (dlong >>> 8) & 0xff; + secbuf[35 + i] = dlong & 0xff; + chksum ^= odd ^ even; + chksum &= 0xffffffff; + } + if (chksum) { + BUG.info('DF%d.decode_buffer() sector %d, data checksum error', this.num, trackoffs); + if (filetype == ADF_EXT2) return 4; + continue; + } + mbuf += 256; + sectable[trackoffs] = 1; + secwritten++; + + for (i = 0; i < 512; i++) this.writebuffer[trackoffs * 512 + i] = secbuf[32 + i]; //memcpy (writebuffer + trackoffs * 512, secbuf + 32, 512); + } + if (filetype == ADF_EXT2 && (secwritten == 0 || secwritten < 0)) + return 5; + if (secwritten == 0) BUG.info('DF%d.decode_buffer() unsupported format', this.num); + if (secwritten < 0) BUG.info('DF%d.decode_buffer() sector labels ignored', this.num); + + return 0; + } + + this.write_adf_amigados = function() { + //var drvsec, i; + //var sectable[MAX_SECTORS]; + + if (this.decode_buffer(0)) //drv->bigmfmbuf, drv->cyl, drv->num_secs, drv->ddhd, drv->filetype, &drvsec, sectable, 0)) + return 2; + //if (!drvsec) return 2; + + /*for (i = 0; i < drvsec; i++) { + zfile_fseek (drv->diskfile, drv->trackdata[drv->cyl * 2 + AMIGA.disk.side].offs + i * 512, SEEK_SET); + zfile_fwrite (writebuffer + i * 512, sizeof (var), 512, drv->diskfile); + }*/ + for (var i = 0; i < this.num_secs; i++) { + var offset = this.trackdata[this.cyl * 2 + AMIGA.disk.side].offs + i * 512; + for (var j = 0; j < 512; j++) + this.diskfile[offset + j] = this.diskdata[offset + j] = this.writebuffer[i * 512 + j]; + } + return 0; + } + + this.write_data = function() { + var ret = -1; + var tr = this.cyl * 2 + AMIGA.disk.side; + + if (this.is_writeprotected() || this.trackdata[tr].type == TRACK_NONE) { + this.buffered_side = 2; + return; + } + //if (this.writediskfile) drive_write_ext2 (this.bigmfmbuf, this.writediskfile, &this.writetrackdata[tr], LONGWRITEMODE ? dsklength2 * 8 : this.tracklen); + + switch (this.filetype) { + case ADF_NORMAL: { + if (this.write_adf_amigados()) { + //notify_user (NUMSG_NEEDEXT2); + } + return; + } + } + this.tracktiming[0] = 0; + } + + this.is_unformatted = function() { + var tr = this.cyl * 2 + AMIGA.disk.side; + if (tr >= this.num_tracks) return true; + if (this.filetype == ADF_EXT2 && this.trackdata[tr].bitlen == 0 && this.trackdata[tr].type != TRACK_AMIGADOS) + return true; + if (this.trackdata[tr].type == TRACK_NONE) + return true; + return false; + } + + this.vsync = function() { + if (this.dskready_down_time > 0) + this.dskready_down_time--; + /* emulate drive motor turn on time */ + if (this.dskready_up_time > 0 && !this.is_empty()) { + if ((--this.dskready_up_time) == 0 && !this.motoroff) + this.dskready = true; + } + /* delay until new disk image is inserted */ + if (this.dskchange_time) { + if ((--this.dskchange_time) == 0) + this.insert(); + } + } +} + +function Disk() { + this.side = 0 + this.direction = 0; + var selected = 15; + var disabled = 0; + var dskdmaen = DSKDMA_OFF; + var dsklength = 0; + var dsklength2 = 0; + var dsklen = 0; + var dskbytr_val = 0; + var dskpt = 0; + var fifo = new Array(3); for (var i = 0; i < 3; i++) fifo[i] = 0; + var fifo_inuse = new Array(3); for (var i = 0; i < 3; i++) fifo_inuse[i] = 0; + var fifo_filled = false; + var dma_enable = false; + var bitoffset = 0; + var syncoffset = 0; + var word = 0; + var dsksync = 0; + var dsksync_cycles = 0; + var disk_hpos = 0; + var disk_jitter = 0; + var indexdecay = 0; + var prev_data = 0; + var prev_step = 0; + var linecounter = 0; + var random_bits_min = 1; + var random_bits_max = 3; + var ledstate = new Array(MAX_FLOPPY_DRIVES); for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) ledstate[i] = false; + var floppy = new Array(MAX_FLOPPY_DRIVES); for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) floppy[i] = new Drive(i); + + this.setup = function() {} + + this.reset = function() { + disk_hpos = 0; + dskdmaen = DSKDMA_OFF; + disabled = 0; + for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) { + floppy[i].reset(); + ledstate[i] = false; + AMIGA.config.hooks.floppy_motor(i, false); + AMIGA.config.hooks.floppy_step(i, floppy[i].cyl); + } + for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) { + this.eject(i); + this.insert(i); + } + } + + this.rand_shifter = function() { + var r = ((uaerand () >>> 4) & 7) + 1; + while (r-- > 0) { + word <<= 1; + word |= (uaerand () & 0x1000) ? 1 : 0; + bitoffset++; + bitoffset &= 15; + } + } + + this.setdskchangetime = function(num, dsktime) { + if (floppy[num].dskchange_time > 0) + return; + + for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) { + if (floppy[i].num != num && floppy[i].dskchange_time > 0 && floppy[i].dskchange_time + 1 >= dsktime) + dsktime = floppy[i].dskchange_time + 1; + } + floppy[num].dskchange_time = dsktime; + //BUG.info('Disk.setdskchangetime() delayed insert enable %d', dsktime); + } + + this.insert2 = function(num, forced) { + //BUG.info('Disk.insert() DF%d', num); + + if (AMIGA.config.floppy.drive[num].name && AMIGA.config.floppy.drive[num].data) { + floppy[num].diskdata = new Uint8Array(AMIGA.config.floppy.drive[num].data.length); + for (var i = 0; i < AMIGA.config.floppy.drive[num].data.length; i++) + floppy[num].diskdata[i] = AMIGA.config.floppy.drive[num].data.charCodeAt(i) & 0xff; + } + + if (forced) { + if (!floppy[num].is_empty()) + floppy[num].eject(); + floppy[num].insert(null); + return; + } + + if (!floppy[num].is_empty() || floppy[num].dskchange_time > 0) { + floppy[num].eject(); + this.setdskchangetime(num, 100); + } else + this.setdskchangetime(num, 1); + } + this.insert = function(num) { + this.insert2(num, false); + } + + this.eject = function(num) { + floppy[num].eject(); + floppy[num].diskdata = null; + } + + this.is_empty = function(num) { + return floppy[num].is_empty(); + } + + this.select_fetch = function(data) { + selected = (data >> 3) & 15; + this.side = 1 - ((data >> 2) & 1); + this.direction = (data >> 1) & 1; + } + + this.select_set = function(data) { + prev_data = data; + prev_step = data & 1; + + this.select_fetch(data); + } + + this.select = function(data) { + //BUG.info('Disk.select() $%02x', data); + var step_pulse, prev_selected, dr; + + prev_selected = selected; + this.select_fetch(data); + step_pulse = data & 1; + + if ((prev_data & 0x80) != (data & 0x80)) { + for (dr = 0; dr < 4; dr++) { + if (floppy[dr].indexhackmode > 1 && !(selected & (1 << dr))) { + floppy[dr].indexhack = 1; + BUG.info('Disk.select() indexhack!'); + } + } + } + if (prev_step != step_pulse) { + prev_step = step_pulse; + if (prev_step) { + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (!((prev_selected | disabled) & (1 << dr))) { + floppy[dr].step(); + if (floppy[dr].indexhackmode > 1 && (data & 0x80)) + floppy[dr].indexhack = 1; + } + } + } + } + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (!(selected & (1 << dr)) && (prev_selected & (1 << dr)) ) { + floppy[dr].drive_id_scnt++; + floppy[dr].drive_id_scnt &= 31; + floppy[dr].idbit = (floppy[dr].drive_id & (1 << (31 - floppy[dr].drive_id_scnt))) ? 1 : 0; + + if (!(disabled & (1 << dr))) { + if ((prev_data & 0x80) == 0 || (data & 0x80) == 0) + floppy[dr].set_motor(0); /* motor off: if motor bit = 0 in prevdata or data -> turn motor on */ + else if (prev_data & 0x80) + floppy[dr].set_motor(1); /* motor on: if motor bit = 1 in prevdata only (motor flag state in data has no effect) -> turn motor off */ + } + if (dr == 0) + floppy[dr].idbit = 0; + } + } + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + var state = (!(selected & (1 << dr))) | !floppy[dr].motoroff; + if (ledstate[dr] != state) { + ledstate[dr] = state; + AMIGA.config.hooks.floppy_motor(dr, ledstate[dr]); + } + } + prev_data = data; + } + + this.status = function() { + var st = 0x3c; + + for (var dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (!((selected | disabled) & (1 << dr))) { + if (floppy[dr].is_running()) { + if (floppy[dr].dskready && !floppy[dr].indexhack) + st &= ~0x20; + } else { + if (dr > 0) { + if (floppy[dr].idbit) + st &= ~0x20; + } else { + /* non-ID internal drive: mirror real dskready */ + if (floppy[dr].dskready) + st &= ~0x20; + } + /* dskrdy needs some cycles after switching the motor off.. (Pro Tennis Tour) */ + if (dr == 0 && floppy[dr].motordelay) + st &= ~0x20; + } + if (floppy[dr].is_track0()) + st &= ~0x10; + if (floppy[dr].is_writeprotected()) + st &= ~8; + if (floppy[dr].dskchange && AMIGA.config.floppy.drive[dr].type != SAEV_Config_Floppy_Type_525_SD) + st &= ~4; + } else if (!(selected & (1 << dr))) { + if (floppy[dr].idbit) + st &= ~0x20; + } + } + //BUG.info('Disk.status() $%02x', st); + return st; + } + + this.fetchnextrevolution = function(num) { + floppy[num].trackspeed = floppy[num].get_floppy_speed2(); + } + + this.handler = function(data) { + var flag = data & 255; + var disk_sync_cycle = data >> 8; + //BUG.info('Disk.handler() data $%x, flag %d, disk_sync_cycle %d', data, flag, disk_sync_cycle); + + //AMIGA.events.eventtab[EV_DISK].active = false; + AMIGA.events.event2_remevent(EV2_DISK); + + this.update(disk_sync_cycle); + + if (flag & (DISK_REVOLUTION << 0)) this.fetchnextrevolution(0); + if (flag & (DISK_REVOLUTION << 1)) this.fetchnextrevolution(1); + if (flag & (DISK_REVOLUTION << 2)) this.fetchnextrevolution(2); + if (flag & (DISK_REVOLUTION << 3)) this.fetchnextrevolution(3); + if (flag & DISK_WORDSYNC) + AMIGA.INTREQ(INT_DSKSYN); + if (flag & DISK_INDEXSYNC) { + if (!indexdecay) { + indexdecay = 2; + AMIGA.cia.setICR(CIA_B, 0x10, null); + } + } + } + + this.update_jitter = function() { + if (random_bits_max > 0) + disk_jitter = ((uaerand() >>> 4) % (random_bits_max - random_bits_min + 1)) + random_bits_min; + else + disk_jitter = 0; + } + + this.updatetrackspeed = function(num, mfmpos) { + if (dskdmaen < DSKDMA_WRITE) { + var t = floppy[num].tracktiming[Math.floor(mfmpos / 8)]; + floppy[num].trackspeed = Math.floor(floppy[num].get_floppy_speed2() * t / 1000); + if (floppy[num].trackspeed < 700 || floppy[num].trackspeed > 3000) { + BUG.info('Disk.updatetrackspeed() corrupted trackspeed value %d', floppy[num].trackspeed); + floppy[num].trackspeed = 1000; + } + } + } + + this.fifostatus = function() { + if (fifo_inuse[0] && fifo_inuse[1] && fifo_inuse[2]) + return 1; + else if (!fifo_inuse[0] && !fifo_inuse[1] && !fifo_inuse[2]) + return -1; + return 0; + } + + this.dmafinished = function() { + //BUG.info('Disk.dmafinished()'); + AMIGA.INTREQ(INT_DSKBLK); + //LONGWRITEMODE = 0; + dskdmaen = DSKDMA_OFF; + dsklength = 0; + } + + this.readdma = function() { + if (AMIGA.dmaen(DMAF_DSKEN) && bitoffset == 15 && dma_enable && dskdmaen == DSKDMA_READ && dsklength >= 0) { + if (dsklength > 0) { + if (dsklength == 1 && dsklength2 == 1) { + this.dmafinished(); + return 0; + } + /* fast disk modes, just flush the fifo */ + if (AMIGA.config.floppy.speed > SAEV_Config_Floppy_Speed_Original && fifo_inuse[0] && fifo_inuse[1] && fifo_inuse[2]) { + while (fifo_inuse[0]) { + var w = this.DSKDATR(); + AMIGA.mem.store16(dskpt, w); + dskpt += 2; + } + } + if (this.fifostatus() > 0) { + BUG.info('Disk.readdma() fifo overflow detected, retrying...'); + return -1; + } else { + this.DSKDAT(word); + dsklength--; + } + } + return 1; + } + return 0; + } + + this.update_read_nothing = function(floppybits) { + //BUG.info('Disk.update_read_nothing() floppybits %d', floppybits); + + while (floppybits >= get_floppy_speed()) { + word <<= 1; + this.readdma(); + word &= 0xffff; + if ((bitoffset & 7) == 7) { + dskbytr_val = word & 0xff; + dskbytr_val |= 0x8000; + } + bitoffset++; + bitoffset &= 15; + floppybits -= get_floppy_speed(); + } + } + + /*static this.read_floppy_data (struct zfile *diskfile, trackid *tid, var offset, var *dst, var len) { + if (len == 0) + return; + zfile_fseek (diskfile, tid->offs + offset, SEEK_SET); + zfile_fread (dst, 1, len, diskfile); + }*/ + + this.update_read = function(num, floppybits) { + //BUG.info('Disk.update_read() DF%d, floppybits %d', num, floppybits); + + while (floppybits >= floppy[num].trackspeed) { + var oldmfmpos = floppy[num].mfmpos; + if (floppy[num].tracktiming[0]) + this.updatetrackspeed(num, floppy[num].mfmpos); + + word <<= 1; + if (!floppy[num].is_empty()) { + if (floppy[num].is_unformatted()) + word |= ((uaerand() & 0x1000) ? 1 : 0); + else + word |= floppy[num].getonebit(floppy[num].mfmpos); + } + word &= 0xffff; + + floppy[num].mfmpos++; + floppy[num].mfmpos %= floppy[num].tracklen; + if (floppy[num].mfmpos == floppy[num].indexoffset) { + //if (floppy[num].indexhack) BUG.info('Disk.update_read() indexhack cleared'); + floppy[num].indexhack = 0; + } + if (floppy[num].mfmpos == floppy[num].skipoffset) { + this.update_jitter(); + floppy[num].mfmpos += disk_jitter; + floppy[num].mfmpos %= floppy[num].tracklen; + } + if (this.readdma() < 0) { + floppy[num].mfmpos = oldmfmpos; + return; + } + if ((bitoffset & 7) == 7) { + dskbytr_val = word & 0xff; + dskbytr_val |= 0x8000; + } + if (word == dsksync) { + dsksync_cycles = AMIGA.events.currcycle + WORDSYNC_TIME * CYCLE_UNIT; + if (dskdmaen != DSKDMA_OFF) { + //if (!dma_enable) BUG.info('Disk.update_read() Sync match, DMA started at %d', floppy[num].mfmpos); + dma_enable = true; + } + if (AMIGA.adkcon & 0x400) { + bitoffset = 15; + } + } + bitoffset++; + bitoffset &= 15; + floppybits -= floppy[num].trackspeed; + } + } + + this.update_write = function(num, floppybits) { + var dr, drives = [0,0,0,0]; + + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + drives[dr] = 0; + if (floppy[dr].motoroff) + continue; + if (selected & (1 << dr)) + continue; + drives[dr] = 1; + } + while (floppybits >= floppy[num].trackspeed) { + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (drives[dr]) { + floppy[dr].mfmpos++; + floppy[dr].mfmpos %= floppy[num].tracklen; + } + } + if (AMIGA.dmaen(DMAF_DSKEN) && dskdmaen == DSKDMA_WRITE && dsklength > 0 && fifo_filled) { + bitoffset++; + bitoffset &= 15; + if (!bitoffset) { + /* fast disk modes, fill the fifo instantly */ + if (AMIGA.config.floppy.speed > SAEV_Config_Floppy_Speed_Original && !fifo_inuse[0] && !fifo_inuse[1] && !fifo_inuse[2]) { + while (!fifo_inuse[2]) { + var w = AMIGA.mem.load16(dskpt); + this.DSKDAT(w); + dskpt += 2; + } + } + if (this.fifostatus() >= 0) { + var w = this.DSKDATR(); + for (dr = 0; dr < MAX_FLOPPY_DRIVES ; dr++) { + if (drives[dr]) { + floppy[dr].bigmfmbuf[floppy[dr].mfmpos >> 4] = w; + floppy[dr].bigmfmbuf[(floppy[dr].mfmpos >> 4) + 1] = 0x5555; + floppy[dr].writtento = 1; + } + } + dsklength--; + if (dsklength <= 0) { + this.dmafinished(); + for (dr = 0; dr < MAX_FLOPPY_DRIVES ; dr++) { + floppy[dr].writtento = 0; + if (floppy[dr].motoroff) + continue; + if (selected & (1 << dr)) + continue; + floppy[dr].write_data(); + } + } + } + } + } + floppybits -= floppy[num].trackspeed; + } + } + + this.doupdate_predict = function(startcycle) { + //BUG.info('Disk.doupdate_predict() startcycle %d', startcycle); + var finaleventcycle = AMIGA.events.maxhpos << 8; + var finaleventflag = 0; + + for (var dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (selected & (1 << dr)) + continue; + else if (floppy[dr].motoroff || !floppy[dr].trackspeed) + continue; + + var diskevent_flag = 0; + var tword = word; + var countcycle = startcycle + (floppy[dr].floppybitcounter % floppy[dr].trackspeed); + var mfmpos = floppy[dr].mfmpos; + while (countcycle < (AMIGA.events.maxhpos << 8)) { + if (floppy[dr].tracktiming[0]) + this.updatetrackspeed(dr, mfmpos); + if (dskdmaen != DSKDMA_WRITE || (dskdmaen == DSKDMA_WRITE && !dma_enable)) { + tword <<= 1; + if (!floppy[dr].is_empty()) { + if (floppy[dr].is_unformatted()) + tword |= ((uaerand() & 0x1000) ? 1 : 0); + else + tword |= floppy[dr].getonebit(mfmpos); + } + tword &= 0xffff; + if (tword == dsksync && dsksync != 0) + diskevent_flag |= DISK_WORDSYNC; + } + mfmpos++; + mfmpos %= floppy[dr].tracklen; + if (mfmpos == 0) + diskevent_flag |= (DISK_REVOLUTION << dr); + if (mfmpos == floppy[dr].indexoffset) + diskevent_flag |= DISK_INDEXSYNC; + if (dskdmaen != DSKDMA_WRITE && mfmpos == floppy[dr].skipoffset) { + this.update_jitter(); + var skipcnt = disk_jitter; + while (skipcnt-- > 0) { + mfmpos++; + mfmpos %= floppy[dr].tracklen; + if (mfmpos == 0) + diskevent_flag |= (DISK_REVOLUTION << dr); + if (mfmpos == floppy[dr].indexoffset) + diskevent_flag |= DISK_INDEXSYNC; + } + } + if (diskevent_flag) + break; + countcycle += floppy[dr].trackspeed; + } + if (floppy[dr].tracktiming[0]) + this.updatetrackspeed(dr, floppy[dr].mfmpos); + if (diskevent_flag && countcycle < finaleventcycle) { + finaleventcycle = countcycle; + finaleventflag = diskevent_flag; + } + } + + if (finaleventflag && (finaleventcycle >>> 8) < AMIGA.events.maxhpos) { + AMIGA.events.event2_newevent(EV2_DISK, (finaleventcycle - startcycle) >>> 8, ((finaleventcycle >>> 8) << 8) | finaleventflag); + + /*var evtime = (finaleventcycle - startcycle) >>> 8; + if (evtime == 0) evtime = 1; + AMIGA.events.eventtab[EV_DISK].active = true; + AMIGA.events.eventtab[EV_DISK].oldcycles = AMIGA.events.currcycle; + AMIGA.events.eventtab[EV_DISK].evtime = AMIGA.events.currcycle + (evtime * CYCLE_UNIT); + AMIGA.events.eventtab[EV_DISK].data = ((finaleventcycle >>> 8) << 8) | finaleventflag; + AMIGA.events.schedule(); + */ + } + } + + this.update = function(tohpos) { + //if (tohpos != 227) BUG.info('Disk.update() disk_hpos %f, to hpos %d', disk_hpos / CYCLE_UNIT, tohpos); + var dr; + var cycles; + var startcycle = disk_hpos; + + if (disk_hpos < 0) { + disk_hpos = -disk_hpos; + return; + } + cycles = (tohpos << 8) - disk_hpos; + if (cycles <= 0) + return; + + disk_hpos += cycles; + if (disk_hpos >= (AMIGA.events.maxhpos << 8)) + disk_hpos %= (1 << 8); + + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (floppy[dr].motoroff || !floppy[dr].tracklen || !floppy[dr].trackspeed) + continue; + floppy[dr].floppybitcounter += cycles; + if (selected & (1 << dr)) { + floppy[dr].mfmpos += Math.floor(floppy[dr].floppybitcounter / floppy[dr].trackspeed); + floppy[dr].mfmpos %= floppy[dr].tracklen; + floppy[dr].floppybitcounter %= floppy[dr].trackspeed; + continue; + } + if (floppy[dr].diskfile) + floppy[dr].fill_bigbuf(0); + floppy[dr].mfmpos %= floppy[dr].tracklen; + } + var didaccess = 0; + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (selected & (1 << dr)) + continue; + else if (floppy[dr].motoroff || !floppy[dr].trackspeed) + continue; + /* write dma and wordsync enabled: read until wordsync match found */ + if (dskdmaen == DSKDMA_WRITE && dma_enable) + this.update_write(dr, floppy[dr].floppybitcounter); + else + this.update_read(dr, floppy[dr].floppybitcounter); + + floppy[dr].floppybitcounter %= floppy[dr].trackspeed; + didaccess = 1; + } + /* no floppy selected but read dma */ + if (!didaccess && dskdmaen == DSKDMA_READ) + this.update_read_nothing(cycles); + + /* instantly finish dma if dsklen==0 and wordsync detected */ + if (dskdmaen != DSKDMA_OFF && dma_enable && dsklength2 == 0 && dsklength == 0) + this.dmafinished(); + + this.doupdate_predict(disk_hpos); + } + + this.dma_debugmsg = function() { + BUG.info('Disk.dma_debugmsg() LEN=%04x (%d) SYNC=%04x PT=%08x ADKCON=%04x', dsklength, dsklength, (AMIGA.adkcon & 0x400) ? dsksync : 0xffff, dskpt, AMIGA.adkcon); + } + + this.start = function() { + fifo_filled = false; + for (var i = 0; i < 3; i++) + fifo_inuse[i] = 0; + + for (var dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (!(selected & (1 << dr))) { + if (dskdmaen == DSKDMA_WRITE) { + floppy[dr].tracklen = LONGWRITEMODE ? FLOPPY_WRITE_MAXLEN : (AMIGA.config.video.ntsc ? 6399 : 6334) * floppy[dr].ddhd * 8 * 2; + floppy[dr].trackspeed = get_floppy_speed(); + floppy[dr].skipoffset = -1; + floppy[dr].updatemfmpos(); + } + + var tr = floppy[dr].cyl * 2 + this.side; + if (floppy[dr].trackdata[tr].type == TRACK_RAW1) { + floppy[dr].mfmpos = 0; + bitoffset = 0; + } + } + floppy[dr].floppybitcounter = 0; + } + dma_enable = (AMIGA.adkcon & 0x400) ? false : true; + } + + this.check_change = function() { + //if (currprefs.floppy_speed != changed_prefs.floppy_speed) currprefs.floppy_speed = changed_prefs.floppy_speed; + /*for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) { + if (currprefs.floppyslots[i].dfxtype != changed_prefs.floppyslots[i].dfxtype) { + currprefs.floppyslots[i].dfxtype = changed_prefs.floppyslots[i].dfxtype; + floppy[i].reset(); + } + }*/ + } + + this.vsync = function() { + this.check_change(); + + for (var i = 0; i < MAX_FLOPPY_DRIVES; i++) { + //if (drv->dskchange_time == 0 && _tcscmp (currprefs.floppyslots[i].df, changed_prefs.floppyslots[i].df)) this.insert(i, changed_prefs.floppyslots[i].df); + floppy[i].vsync(); + } + } + + this.hsync = function() { + for (var dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (floppy[dr].steplimit) + floppy[dr].steplimit--; + } + if (indexdecay) + indexdecay--; + if (linecounter) { + if (!(--linecounter)) + this.dmafinished(); + return; + } + this.update(AMIGA.events.maxhpos); + } + + this.update_adkcon = function(v) { + var vold = AMIGA.adkcon; + var vnew = AMIGA.adkcon; + if (v & 0x8000) + vnew |= v & 0x7FFF; + else + vnew &= ~v; + + if ((vnew & 0x400) && !(vold & 0x400)) + bitoffset = 0; + } + + this.motordelay_func = function(unit) { + //BUG.info('Disk.motordelay_func(%d)', unit); + floppy[unit].motordelay = false; + } + + this.DSKLEN = function(v, hpos) + { + //BUG.info('Disk.DSKLEN() $%04x', v); + var dr, prev = dsklen; + + this.update(hpos); + + if ((v & 0x8000) && (dsklen & 0x8000)) { + dskdmaen = DSKDMA_READ; + this.start(); + } + if (!(v & 0x8000)) { + if (dskdmaen != DSKDMA_OFF) { + if (dskdmaen == DSKDMA_READ) + BUG.info('Disk.DSKLEN() warning: Disk read DMA aborted, %d words left', dsklength); + else if (dskdmaen == DSKDMA_WRITE) { + BUG.info('Disk.DSKLEN() warning: Disk write DMA aborted, %d words left', dsklength); + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (floppy[dr].writtento) floppy[dr].write_data(); + } + } + dskdmaen = DSKDMA_OFF; + } + } + dsklen = v; + dsklength2 = dsklength = dsklen & 0x3fff; + + if (dskdmaen == DSKDMA_OFF) + return; + if (dsklength == 0 && dma_enable) { + this.dmafinished(); + return; + } + if ((v & 0x4000) && (prev & 0x4000)) { + if (dsklength == 0) + return; + if (dsklength == 1) { + this.dmafinished(); + return; + } + dskdmaen = DSKDMA_WRITE; + this.start(); + } + + var motormask = 0; + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + floppy[dr].writtento = 0; + if (floppy[dr].motoroff) + continue; + motormask |= 1 << dr; + if ((selected & (1 << dr)) == 0) + break; + } + var noselected = dr == 4; + + /* Try to make floppy access from Kickstart faster. */ + if (dskdmaen != DSKDMA_READ && dskdmaen != DSKDMA_WRITE) + return; + + /* no turbo mode if any selected drive has non-standard ADF */ + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + if (selected & (1 << dr)) + continue; + if (floppy[dr].filetype != ADF_NORMAL) + break; + } + if (dr < MAX_FLOPPY_DRIVES) + return; + + { + var done = false; + for (dr = 0; dr < MAX_FLOPPY_DRIVES; dr++) { + var pos, i; + + if (selected & (1 << dr)) + continue; + else if (floppy[dr].motoroff) + continue; + else if (!floppy[dr].useturbo && AMIGA.config.floppy.speed != SAEV_Config_Floppy_Speed_Turbo) + continue; + + pos = floppy[dr].mfmpos & ~15; + floppy[dr].fill_bigbuf(0); + + if (dskdmaen == DSKDMA_READ) { //TURBO read + if (AMIGA.adkcon & 0x400) { + for (i = 0; i < floppy[dr].tracklen; i += 16) { + pos += 16; + pos %= floppy[dr].tracklen; + if (floppy[dr].bigmfmbuf[pos >> 4] == dsksync) { + pos += 16; + pos %= floppy[dr].tracklen; + break; + } + } + if (i >= floppy[dr].tracklen) + return; + } + while (dsklength-- > 0) { + AMIGA.mem.store16(dskpt, floppy[dr].bigmfmbuf[pos >> 4]); + dskpt += 2; + pos += 16; + pos %= floppy[dr].tracklen; + } + AMIGA.INTREQ(INT_DSKSYN); + done = true; + } else if (dskdmaen == DSKDMA_WRITE) { //TURBO write + for (i = 0; i < dsklength; i++) { + var w = AMIGA.mem.load16(dskpt + i * 2); + floppy[dr].bigmfmbuf[pos >> 4] = w; + pos += 16; + pos %= floppy[dr].tracklen; + } + floppy[dr].write_data(); + done = true; + } + } + if (!done && noselected) { + while (dsklength-- > 0) { + if (dskdmaen == DSKDMA_WRITE) + AMIGA.mem.load16(dskpt); + else + AMIGA.mem.store16(dskpt, 0); + dskpt += 2; + } + AMIGA.INTREQ(INT_DSKSYN); + done = true; + } + if (done) { + linecounter = 2; + dskdmaen = DSKDMA_OFF; + } + } + } + + this.DSKBYTR = function(hpos) { + this.update(hpos); + + var v = dskbytr_val; + dskbytr_val &= ~0x8000; + if (word == dsksync && AMIGA.events.cycles_in_range(dsksync_cycles)) + v |= 0x1000; + if (dskdmaen != DSKDMA_OFF && AMIGA.dmaen(DMAF_DSKEN)) + v |= 0x4000; + if (dsklen & 0x4000) + v |= 0x2000; + + //BUG.info('Disk.DSKBYTR() %x', v); + return v; + } + + this.DSKSYNC = function(v, hpos) { + if (v == dsksync) + return; + + this.update(hpos); + dsksync = v; + } + + this.DSKDAT = function(v) { + if (fifo_inuse[2]) { + BUG.info('Disk.DSKDAT() FIFO overflow!'); + return; + } + fifo_inuse[2] = fifo_inuse[1]; + fifo[2] = fifo[1]; + fifo_inuse[1] = fifo_inuse[0]; + fifo[1] = fifo[0]; + fifo_inuse[0] = dskdmaen == DSKDMA_WRITE ? 2 : 1; + fifo[0] = v; + fifo_filled = true; + } + + this.DSKDATR = function() { + var i, v = 0; + + for (i = 2; i >= 0; i--) { + if (fifo_inuse[i]) { + fifo_inuse[i] = 0; + v = fifo[i]; + break; + } + } + if (i < 0) + BUG.info('Disk.DSKDATR() FIFO underflow!'); + else if (dskdmaen > 0 && dskdmaen < 3 && dsklength <= 0 && this.fifostatus() < 0) + this.dmafinished(); + + //BUG.info('Disk.DSKDATR() %x', v); + return v; + } + + this.DSKPTH = function(v) { + dskpt = ((v << 16) | (dskpt & 0xffff)) >>> 0; + } + + this.DSKPTL = function(v) { + dskpt = ((dskpt & 0xffff0000) | v) >>> 0; + } + + this.getpt = function() { + var pt = dskpt; + dskpt += 2; + return pt; + } + + this.dmal = function() { + var dmal = 0; + if (dskdmaen != DSKDMA_OFF) { + if (dskdmaen == DSKDMA_WRITE) { + dmal = (1 + 2) * (fifo_inuse[0] ? 1 : 0) + (4 + 8) * (fifo_inuse[1] ? 1 : 0) + (16 + 32) * (fifo_inuse[2] ? 1 : 0); + dmal ^= 63; + if (dsklength == 2) + dmal &= ~(16 + 32); + if (dsklength == 1) + dmal &= ~(16 + 32 + 4 + 8); + } else { + dmal = 16 * (fifo_inuse[0] ? 1 : 0) + 4 * (fifo_inuse[1] ? 1 : 0) + 1 * (fifo_inuse[2] ? 1 : 0); + } + } + return dmal; + } +} diff --git a/sae/events.js b/sae/events.js new file mode 100644 index 0000000..7506d0d --- /dev/null +++ b/sae/events.js @@ -0,0 +1,803 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Event() { + this.active = false; + this.evtime = 0; + this.oldcycles = 0; + this.handler = function(v) {}; +} + +function Event2() { + this.active = false; + this.evtime = 0; + this.handler = function(v) {}; + this.data = null; +} + +function Events() { + const SYNCBASE = 1000; + + this.lofChanged = false; + this.lofStore = 0; + this.lof = 0; + this.lol = 0; + + this.vpos = 0; + this.vpos_previous = 0; + this.hpos_previous = 0; + + this.maxhpos = 0; + this.maxvpos = 0; + this.hz = 0; + + var dmal = 0 + var dmal_hpos = 0; + + var vsynctimebase = 0; + var vsyncmintime = 0; + var vsynctimeperline = 0; + var vsyncmaxtime = 0; + var vsyncwaittime = 0; + var vsync_rendered = false; + var is_syncline = 0; + var is_syncline_end = 0; + + var frameskip = new mavg(128); + var frameskiptime = 0; + + var counter = { + lasttime:0, + frametime:0, + frames:0, + fps:new mavg(10), + idle:new mavg(10), + idletime:0, + bogus:0 + }; + this.timeframes = 0; + + this.eventtab = null; + this.eventtab2 = null; + this.currcycle = 0; + var nextEvent = 0; + var nextEvent2 = EV2_MISC; + + this.setup = function() { + if (this.eventtab === null) { + this.eventtab = new Array(EV_MAX); + for (var i = 0; i < EV_MAX; i++) + this.eventtab[i] = new Event(); + + this.eventtab[EV_CIA].handler = function() { AMIGA.cia.handler(); } + this.eventtab[EV_AUDIO].handler = function() { AMIGA.audio.handler(); } + this.eventtab[EV_MISC].handler = function() { var stack = { recursive:0, dorecheck:false }; AMIGA.events.MISC_handler(stack); } + this.eventtab[EV_HSYNC].handler = function() { AMIGA.events.hSync(); } + //this.eventtab[EV_COPPER].handler = function() { AMIGA.copper.handler(); } + //this.eventtab[EV_BLITTER].handler = function() { AMIGA.blitter.handler(); } + //this.eventtab[EV_RENDER].handler = function() { AMIGA.playfield.hSync(); } + + BUG.info('Events.setup() set %d event handlers', EV_MAX); + } else + BUG.info('Events.setup() event handlers are cached...'); + + if (this.eventtab2 === null) { + this.eventtab2 = new Array(EV2_MAX); + for (var i = 0; i < EV2_MAX; i++) + this.eventtab2[i] = new Event2(); + + //this.eventtab[EV2_BLITTER].handler = function() { AMIGA.blitter.handler(); } + //this.eventtab[EV2_DISK].handler = function(data) { AMIGA.disk.handler(data); } + + BUG.info('Events.setup() set %d event2 handlers', EV2_MAX); + } else + BUG.info('Events.setup() event2 handlers are cached...'); + + this.initHz(); + } + + this.reset = function() { + this.lofStore = 1; + this.lof = 1; + this.lol = 0; + this.vpos = 0; + + dmal = 0 + dmal_hpos = 0; + + frameskip.clr(); + frameskiptime = 0; + this.fpscounter_reset(); + + this.initHz(); + + this.currcycle = 0; + + for (var i = 0; i < EV_MAX; i++) { + this.eventtab[i].active = false; + this.eventtab[i].evtime = 0; + this.eventtab[i].oldcycles = 0; + } + for (var i = 0; i < EV2_MAX; i++) { + this.eventtab2[i].active = false; + this.eventtab2[i].evtime = 0; + } + this.eventtab[EV_HSYNC].evtime = this.maxhpos * CYCLE_UNIT; /* 0xe3 */ + this.eventtab[EV_HSYNC].active = true; + /*if (AMIGA.config.video.enabled) { + //this.eventtab[EV_RENDER].evtime = 0x38 * CYCLE_UNIT; + this.eventtab[EV_RENDER].evtime = 0xd4 * CYCLE_UNIT; + this.eventtab[EV_RENDER].active = true; + }*/ + this.schedule(); + } + + /*---------------------------------*/ + + this.initHz = function() { + if (!AMIGA.config.video.ntsc) { + this.maxvpos = 312; + this.maxhpos = 227; + this.hz = 50; + } else { + this.maxvpos = 262; + this.maxhpos = 227; + this.hz = 60; + } + vsynctimebase = SYNCBASE / this.hz; + } + + this.hpos = function() { + return Math.floor((this.currcycle - this.eventtab[EV_HSYNC].oldcycles) * CYCLE_UNIT_INV); + } + + this.cycles_in_range = function(endcycles) { + return (endcycles - this.currcycle > 0); + } + + /*this.get_cycles = function() { + return this.currcycle; + } + + this.set_cycles = function(x) { + this.currcycle = x; + this.eventtab[EV_HSYNC].oldcycles = x; + }*/ + + /*---------------------------------*/ + + this.MISC_handler = function(stack) { + //console.log('MISC_handler() recursive %d', stack.recursive); + var mintime; + var recheck; + var ct = AMIGA.events.currcycle; + + if (stack.recursive) { + stack.dorecheck = true; + return; + } + stack.recursive++; + AMIGA.events.eventtab[EV_MISC].active = false; + recheck = true; + while (recheck) { + recheck = false; + mintime = CYCLE_MAX; + + for (var i = 0; i < EV2_MAX; i++) { + if (AMIGA.events.eventtab2[i].active) { + if (AMIGA.events.eventtab2[i].evtime == ct) { + AMIGA.events.eventtab2[i].active = false; + + if (i == EV2_BLITTER) + AMIGA.blitter.handler(AMIGA.events.eventtab2[i].data); + else if (i == EV2_DISK) + AMIGA.disk.handler(AMIGA.events.eventtab2[i].data); + else + AMIGA.events.eventtab2[i].handler(AMIGA.events.eventtab2[i].data); + + if (stack.dorecheck || AMIGA.events.eventtab2[i].active) { + recheck = true; + stack.dorecheck = false; + } + } else { + var eventtime = AMIGA.events.eventtab2[i].evtime - ct; + if (eventtime < mintime) + mintime = eventtime; + } + } + } + } + if (mintime != CYCLE_MAX) { + AMIGA.events.eventtab[EV_MISC].active = true; + AMIGA.events.eventtab[EV_MISC].oldcycles = ct; + AMIGA.events.eventtab[EV_MISC].evtime = ct + mintime; + AMIGA.events.schedule(); + } + stack.recursive--; + } + + this.event2_newevent_xx = function(no, t, data, func) { + var et = this.currcycle + t; + if (no < 0) { + no = nextEvent2; + for (;;) { + if (!this.eventtab2[no].active) + break; + //if (this.eventtab2[no].evtime == et && this.eventtab2[no].handler == func && this.eventtab2[no].data == data) + if (this.eventtab2[no].evtime == et && this.eventtab2[no].data == data) + break; + + no++; + if (no == EV2_MAX) + no = EV2_MISC; + if (no == nextEvent2) { + BUG.info('event2_newevent_xx() out of event2\'s!'); + return; + } + } + nextEvent2 = no; + } + this.eventtab2[no].active = true; + this.eventtab2[no].evtime = et; + this.eventtab2[no].handler = func; + this.eventtab2[no].data = data; + + var stack = { recursive:0, dorecheck:false }; + this.MISC_handler(stack); + } + + this.event2_newevent_x = function(no, t, data, func) { + if (t <= 0) { + if (no == EV2_BLITTER) + AMIGA.blitter.handler(data); + else if (no == EV2_DISK) + AMIGA.disk.handler(data); + else + func(data); + } else + this.event2_newevent_xx(no, t * CYCLE_UNIT, data, func); + } + + this.event2_newevent2 = function(t, data, func) { + this.event2_newevent_x(-1, t, data, func); + } + + this.event2_newevent = function(no, t, data) { + this.event2_newevent_x(no, t, data, null); //this.eventtab2[no].handler); + } + + this.event2_remevent = function(no) { + this.eventtab2[no].active = false; + } + + /*---------------------------------*/ + + this.schedule = function() { + var mintime = CYCLE_MAX; + + for (var i = 0; i < EV_MAX; i++) { + if (this.eventtab[i].active) { + var evtime = this.eventtab[i].evtime - this.currcycle; + if (evtime < mintime) mintime = evtime; + } + } + nextEvent = this.currcycle + mintime; + } + + this.cycle = function (cycles) { + while ((nextEvent - this.currcycle) <= cycles) { + if (is_syncline) { + var rpt = new Date().getTime(); + var v = rpt - vsyncmintime; + var v2 = rpt - is_syncline_end; + if (v > vsynctimebase || v < -vsynctimebase) v = 0; + if (v < 0 && v2 < 0) return; + is_syncline = 0; + } + + cycles -= nextEvent - this.currcycle; + this.currcycle = nextEvent; + + for (var i = 0; i < EV_MAX; i++) { + if (this.eventtab[i].active && this.eventtab[i].evtime == this.currcycle) + this.eventtab[i].handler(this.eventtab[i].data); + } + this.schedule(); + } + this.currcycle += cycles; + } + + /*---------------------------------*/ + + this.dmal_emu = function(v) { + if (!(AMIGA.dmacon & DMAF_DMAEN)) + return; + + //BUG.info('Events.dmal_emu() %d', v); + var hpos = this.hpos(); + if (v >= 6) { + v -= 6; + var nr = v >>> 1; + var pt = AMIGA.audio.getpt(nr, (v & 1) != 0); + var dat = AMIGA.mem.load16_chip(pt); + AMIGA.audio.AUDxDAT(nr, dat); + } else { + var w = v & 1; + var pt = AMIGA.disk.getpt(); + if (w) { + if (AMIGA.disk.fifostatus() <= 0) { + var dat = AMIGA.mem.load16_chip(pt); + AMIGA.disk.DSKDAT(dat); + } + } else { + if (AMIGA.disk.fifostatus() >= 0) { + var dat = AMIGA.disk.DSKDATR(); + AMIGA.mem.store16_chip(pt, dat); + } + } + } + } + + this.dmal_func = function(v) { + this.dmal_emu(v); + this.dmal_handle(0); + } + + this.dmal_func2 = function(v) { + while (dmal) { + if (dmal & 3) + this.dmal_emu(dmal_hpos + ((dmal & 2) ? 1 : 0)); + dmal_hpos += 2; + dmal >>>= 2; + } + } + + this.dmal_handle = function(hp) { + if (!dmal) + return; + if (AMIGA.config.cpu.exact) { + while (dmal) { + if (dmal & 3) + break; + hp += 2; + dmal >>>= 2; + dmal_hpos += 2; + } + this.event2_newevent2(hp, dmal_hpos + ((dmal & 2) ? 1 : 0), function(v) { AMIGA.events.dmal_func(v); }); + dmal &= ~3; + } else { + this.event2_newevent2(hp, 13, function(v) { AMIGA.events.dmal_func2(v); }); + } + } + + this.dmal_hsync = function() { + if (dmal) BUG.info('Events.dmal_hsync() DMAL error!? %04x', dmal); + dmal = AMIGA.audio.dmal(); + dmal <<= 6; + dmal |= AMIGA.disk.dmal(); + if (!dmal) + return; + //BUG.info('Events.dmal_hsync() %04x', dmal); + dmal_hpos = 0; + this.dmal_handle(7); + } + + /*---------------------------------*/ + + this.fpscounter_reset = function() { + counter.frames = 0; + counter.lasttime = new Date().getTime(); + counter.fps.clr(); + counter.idle.clr(); + counter.idletime = 0; + counter.bogus = 2; + this.timeframes = 0; + } + + this.fpscounter = function(frameok) { + var now = new Date().getTime(); + var last = now - counter.lasttime; + counter.lasttime = now; + + if (counter.bogus || last < 0) + return; + + counter.fps.set(last / 10); + counter.idle.set(counter.idletime / 10); + counter.idletime = 0; + + counter.frametime += last; + counter.frames++; + this.timeframes++; + if ((counter.frames & 7) == 0) { + var idle = 1000 - (counter.idle.average == 0 ? 0 : counter.idle.average * 1000 / vsynctimebase); + var fps = counter.fps.average == 0 ? 0 : SYNCBASE * 10 / counter.fps.average; + if (fps > 9999) fps = 9999; + if (idle < 0) idle = 0; + if (idle > 100 * 10) idle = 100 * 10; + if (fps < this.hz * 10) idle *= (this.hz * 10 / fps); + + if ((counter.frames & 15) == 0) { + AMIGA.config.hooks.fps(Math.round(fps * 0.1)); + AMIGA.config.hooks.cpu(Math.round(idle * 0.1)); + } + } + } + + var lastskiptime = new Date().getTime(); + this.testFrameSkip = function() { + var now = new Date().getTime(); + var time = now - lastskiptime; + lastskiptime = now; + + var fps = Math.floor(SYNCBASE / (time * 0.1)) * 0.1; + if (fps < this.hz) { + if (AMIGA.playfield.frame.skip) { + if (++AMIGA.playfield.frame.skipcount >= MAX_FRAMESKIP_COUNT) { + if (!AMIGA.playfield.state.ilace || (AMIGA.playfield.state.ilace && this.lof == 1)) { + AMIGA.playfield.frame.skipcount = 0; + AMIGA.playfield.frame.skip = false; + //BUG.info('Events.vSyncPost() force frameskip disable'); + } + } + } else { + if (!AMIGA.playfield.state.ilace || (AMIGA.playfield.state.ilace && this.lof == 0)) + AMIGA.playfield.frame.skip = true; + } + } else { + AMIGA.playfield.frame.skip = false; + AMIGA.playfield.frame.skipcount = 0; + } + //AMIGA.playfield.frame.skip = (AMIGA.playfield.frame.count & 1) == 1; + } + + /*---------------------------------*/ + + /*function getTime() { + return new Date().getTime(); + }*/ + + this.sleep = function(ms) { + //console.log('sleep'); + var start = new Date().getTime(); + while ((new Date().getTime() - start) < ms); + } + + this.rpt_vsync = function(adjust) { + var t = new Date().getTime(); + var v = t - vsyncwaittime + adjust; + if (v > SYNCBASE || v < -SYNCBASE) { + vsyncmintime = vsyncmaxtime = vsyncwaittime = t; + v = 0; + } + return v; + } + + this.framewait = function() { + var curr_time; + var clockadjust = 0; + var vstb = vsynctimebase; + + is_syncline = 0; + + var frameskip_avg = frameskip.set(frameskiptime); + frameskiptime = 0; + //BUG.info(frameskip_avg); + + if (AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum) { + curr_time = new Date().getTime(); + + var adjust = 0; + if (curr_time - vsyncwaittime > 0 && curr_time - vsyncwaittime < vstb / 2) + adjust += curr_time - vsyncwaittime; + adjust += clockadjust; + + var max = Math.floor(vstb * (1.0 - adjust)); + vsyncwaittime = curr_time + vstb - adjust; + vsyncmintime = curr_time; + + if (max < 0) { + max = 0; + vsynctimeperline = 1; + } else { + vsynctimeperline = Math.floor(max / (this.maxvpos + 1)); + } + vsyncmaxtime = curr_time + max; + + //BUG.info("%06d:%06d/%06d", adjust, vsynctimeperline, vstb); + } else { + var start = new Date().getTime(); + while (true) { + var v = this.rpt_vsync(clockadjust);// / (SYNCBASE / 1000.0); + if (v >= -4) break; + this.sleep(2); + } + curr_time = start = new Date().getTime(); + while (this.rpt_vsync(clockadjust) < 0); + counter.idletime += new Date().getTime() - start; + + curr_time = new Date().getTime(); + vsyncmintime = curr_time; + vsyncmaxtime = vsyncwaittime = curr_time + vstb; + + var t = frameskip_avg; + vsynctimeperline = (vstb - t) / 3; + if (vsynctimeperline < 0) + vsynctimeperline = 0; + else if (vsynctimeperline > vstb / 3) + vsynctimeperline = vstb / 3; + } + } + + this.is_last_line = function() { + return this.vpos + 1 == this.maxvpos + this.lof_store; + } + this.framewait2 = function() { + if (AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum) { + if (this.is_last_line()) { + vsyncmintime = vsyncmaxtime; + is_syncline_end = new Date().getTime() + vsynctimebase; + is_syncline = 1; + } else { + vsyncmintime += vsynctimeperline; + is_syncline = 0; + if (vsyncmaxtime - vsyncmintime > 0) { + if (vsyncwaittime - vsyncmintime > 0) { + var rpt = new Date().getTime(); + if (vsyncmintime - rpt > 0) { + is_syncline = 1; + is_syncline_end = rpt + vsynctimeperline; + } + } + } + } + } else { + if (this.is_last_line() && (this.vpos == Math.floor(this.maxvpos * 1 / 3) || this.vpos == Math.floor(this.maxvpos * 2 / 3))) { + vsyncmintime += vsynctimeperline; + var rpt = new Date().getTime(); + while (vsyncmintime - (rpt + vsynctimebase / 10) > 0 && vsyncmintime - rpt < vsynctimebase) { + this.sleep(1); + rpt = new Date().getTime(); + } + } + } + } + + /*---------------------------------*/ + + this.vSyncPre = function() { + if (counter.bogus > 0) + counter.bogus--; + + AMIGA.cia.vSyncPre(); + + if (!vsync_rendered) { + var start = new Date().getTime(); + if (AMIGA.config.video.enabled) { + AMIGA.playfield.vSync(); + if (AMIGA.config.video.skip) this.testFrameSkip(); + } + frameskiptime += new Date().getTime() - start; + } + + this.framewait(); + this.fpscounter(); + + vsync_rendered = false; + } + + this.vSyncPost = function() { + AMIGA.disk.vsync(); + + if (AMIGA.playfield.bplcon0 & 4) + this.lofStore = this.lofStore ? 0 : 1; + this.lof = this.lofStore; + + //if ((this.beamcon0 & (0x20|0x80)) != (this.new_beamcon0 & (0x20|0x80)) || this.hack_vpos) + //if ((this.beamcon0 & (0x20|0x80)) != (this.new_beamcon0 & (0x20|0x80)) || (abs (vpos_count - vpos_count_prev) > 1)) this.initHz(); + //if (this.lofChanged) this.compute_vsynctime(); + + this.lofChanged = false; + + AMIGA.copper.COPJMP(1, 1); + + if (!AMIGA.playfield.state.ilace || (AMIGA.playfield.state.ilace && this.lof == 1)) + AMIGA.state = CMD_IDLE; + } + + this.hSyncPre = function(isvsync) { + AMIGA.copper.sync_copper_with_cpu(this.maxhpos, 0); + + if (AMIGA.config.video.enabled) + AMIGA.playfield.hSync(); + + AMIGA.disk.hsync(); + + if (AMIGA.config.audio.enabled) + AMIGA.audio.hsync(); + + //AMIGA.cia.hSyncPre(); //empty + + if (AMIGA.config.video.ntsc) { + this.lol ^= 1; + this.maxhpos = 227 + this.lol; + } else { + this.lol = 0; + this.maxhpos = 227; + } + this.vpos++; + if (isvsync) + this.vpos = 0; + + AMIGA.playfield.sprites.clear(); + + this.eventtab[EV_HSYNC].oldcycles = this.currcycle; + this.eventtab[EV_HSYNC].evtime = this.maxhpos * CYCLE_UNIT + this.currcycle; + } + + this.hSyncPost = function(isvsync) { + AMIGA.cia.hSyncPost(1); + if (isvsync) + AMIGA.cia.vSyncPost(1); + + AMIGA.custom.last_value = 0xffff; + + //if (!AMIGA.config.blitter.exact && AMIGA.blitter.getState() != BLT_done && AMIGA.dmaen(DMAF_BPLEN))// && diwstate == DIW_waiting_stop) AMIGA.blitter.slowdown(); + + if (isvsync) { + this.vpos = 0; + this.vSyncPost(); + } + + if (AMIGA.config.chipset.agnus == AGNUS_8361) { + if (this.vpos == 1) + AMIGA.send_interrupt(5, 1 * CYCLE_UNIT); + } else { + if (this.vpos == 0) + AMIGA.send_interrupt(5, 1 * CYCLE_UNIT); + } + + this.dmal_hsync(); + + AMIGA.copper.reset2(); + //AMIGA.copper.check(2); + + this.framewait2(); + + AMIGA.playfield.sprites.cycle(0); + + if (this.vpos + 1 == this.maxvpos + this.lof_store && AMIGA.config.cpu.speed == SAEV_Config_CPU_Speed_Maximum && !vsync_rendered) { + var start = new Date().getTime(); + if (AMIGA.config.video.enabled) { + AMIGA.playfield.vSync(); + if (AMIGA.config.video.skip) this.testFrameSkip(); + } + frameskiptime += new Date().getTime() - start; + vsync_rendered = true; + } + } + + this.hSync = function() { + var cvp = this.vpos + 1; + var mvp = this.maxvpos + this.lofStore; + var vsync = cvp == mvp || cvp == mvp + 1; + + this.hSyncPre(vsync); + if (vsync) + this.vSyncPre(); + + this.hSyncPost(vsync); + } + + /*---------------------------------*/ + + this.GETVPOS = function() { + return ((AMIGA.playfield.bplcon0 & 2) ? this.vpos_previous : this.vpos); + } + this.GETHPOS = function() { + return ((AMIGA.playfield.bplcon0 & 2) ? this.hpos_previous : this.hpos()); + } + + this.VPOSR = function() { + var chipset = 0; + + switch (AMIGA.config.chipset.agnus) { + case AGNUS_8367: + case AGNUS_8371: + chipset = 0; + break; + case AGNUS_8361: + case AGNUS_8370: + chipset = 10; + break; + case AGNUS_8372: { + if (AMIGA.config.chipset.agnus_rev <= 4) + chipset = AMIGA.config.video.ntsc ? 30 : 20; + else + chipset = AMIGA.config.video.ntsc ? 31 : 21; + break; + } + /*AGA + case AGNUS_8374: { + if (AMIGA.config.chipset.agnus_rev <= 2) + chipset = AMIGA.config.video.ntsc ? 32 : 22; + else + chipset = AMIGA.config.video.ntsc ? 33 : 23; + break; + }*/ + } + var vp = this.GETVPOS(); + var hp = this.GETHPOS(); + + if (hp + 3 >= this.maxhpos) { + vp++; + if (vp >= this.maxvpos + this.lofStore) + vp = 0; + } + vp = (vp >> 8) & 7; + vp &= 1; + + //vp = (vp >> 8) & 1; + + var vpos = (this.lofStore << 15) | (chipset << 8) | (this.lol << 7) | vp; + + //BUG.info('Custom.VPOSR() vp %d, hp %d, vpos $%04x', this.vpos, this.hpos(), vpos); + return vpos; + } + + this.VPOSW = function (v) { + //BUG.info('Custom.VPOSW() $%04x', v); + + if (this.lofStore != ((v & 0x8000) ? 1 : 0)) { + this.lofChanged = true; + this.lofStore = (v & 0x8000) ? 1 : 0; + } + if (AMIGA.config.video.ntsc) + this.lol = (v & 0x0080) ? 1 : 0; + + if (this.lofChanged) + return; + + this.vpos &= 0x00ff; + this.vpos |= ((v & 1) << 8); + } + + this.VHPOSW = function (v) { + //BUG.info('Custom.VHPOSW() $%04x', v); + + this.vpos &= 0xff00; + this.vpos |= (v >> 8); + } + + this.VHPOSR = function() { + var vp = this.GETVPOS(); + var hp = this.GETHPOS(); + + if (hp >= this.maxhpos) { + //console.log(hp); + hp -= this.maxhpos; + vp++; + if (vp >= this.maxvpos + this.lofStore) + vp = 0; + } + hp += 1; + if (hp >= this.maxhpos) + hp -= this.maxhpos; + + var vhpos = ((vp & 0xff) << 8) | ((hp >> 1) & 0xff); + + //BUG.info('Custom.VHPOSR() vp %d, hp %d, vhpos $%04x', vp, hp, vhpos); + return vhpos; + } + + this.BEAMCON0 = function (v) { + BUG.info('BEAMCON0() $%04x', v); + } +} diff --git a/sae/expansion.js b/sae/expansion.js new file mode 100644 index 0000000..254d95f --- /dev/null +++ b/sae/expansion.js @@ -0,0 +1,177 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Board_A2058() { + const MEM_8MB = 0x00; + const MEM_4MB = 0x07; + const MEM_2MB = 0x06; + const MEM_1MB = 0x05; + const MEM_512KB = 0x04; + const MEM_256KB = 0x03; + //const MEM_128KB = 0x02; + //const MEM_64KB = 0x01; + + //const SAME_SLOT = 0x08; /* Next card is in the same Slot */ + //const ROM_CARD = 0x10; /* Card has valid ROM */ + const ADD_MEMORY = 0x20; /* Add Memory to List of Free Ram */ + + const ZORRO2 = 0xc0; /* Type of Expansion Card */ + //const ZORRO3 = 0x80; + + const CARE_ADDR = 0x80; /* Adress HAS to be $200000-$9fffff */ + + const VENDOR_COMMODORE = 514; + const PRODUCT_A2058 = 10; + + this.info = function() { + //BUG.info('Board_A2058.info()'); + var type; + switch (AMIGA.mem.fast.size) { + case 0x080000: type = ZORRO2 + ADD_MEMORY + MEM_512KB; break; + case 0x100000: type = ZORRO2 + ADD_MEMORY + MEM_1MB; break; + case 0x200000: type = ZORRO2 + ADD_MEMORY + MEM_2MB; break; + case 0x400000: type = ZORRO2 + ADD_MEMORY + MEM_4MB; break; + case 0x800000: type = ZORRO2 + ADD_MEMORY + MEM_8MB; break; + } + return { + name:'Commodore A2058', + vendor:VENDOR_COMMODORE, + product:PRODUCT_A2058, + serial:1, + type:type, + flags:CARE_ADDR, + rom:0, + ctrl:0 + }; + } +} + +function Board_Dummy() { + this.info = function() { + //BUG.info('Board_Dummy.info()'); + return { + name:null, + vendor:0, + product:0, + serial:0, + type:0, + flags:0, + rom:0, + ctrl:0 + }; + } +} + +function Expansion() { + const MAX_EXPANSION_BOARDS = 5; + + var mem = { + data:null, + lo:0, + hi:0 + }; + var boards = []; + var board = 0; + + this.setup = function() { + mem.data = new Uint16Array(0x8000); + boards = []; + if (AMIGA.mem.fast.size > 0) + boards[0] = new Board_A2058(); + else + boards[0] = new Board_Dummy(); + + for (var i = 1; i < MAX_EXPANSION_BOARDS; i++) + boards[i] = new Board_Dummy(); + } + + this.reset = function() { + board = 0; + this.config(board); + } + + this.clear = function() { + for (var i = 0; i < mem.data.length; i++) + mem.data[i] = 0; + } + + this.write = function (addr, value) { + mem.data[(addr >> 1)] = (value & 0xf0) << 8; + mem.data[(addr >> 1) + 1] = (value & 0x0f) << 12; + } + + this.load8 = function (addr) { + addr &= 0xffff; + var value = (mem.data[addr >>> 1] >> ((addr & 1) ? 0 : 8)) & 0xff; + if (addr == 0 || addr == 2 || addr == 0x40 || addr == 0x42) + return value; + return ~value & 0xff; + } + + this.store8 = function (addr, value) { + switch (addr & 0xff) { + case 0x30: + case 0x32: + mem.hi = 0; + mem.lo = 0; + this.write(0x48, 0x00); + break; + + case 0x48: + mem.hi = value; + //BUG.info('Expansion.store8() board %d done.', board + 1); + ++board; + if (board <= MAX_EXPANSION_BOARDS) + this.config(board); + else + this.clear(); + break; + + case 0x4a: + mem.lo = value; + break; + + case 0x4c: + //BUG.info('Expansion.store8() board %d faild.', board + 1); + ++board; + if (board <= MAX_EXPANSION_BOARDS) + this.config(board); + else + this.clear(); + break; + } + } + + this.config = function(board) { + var info = boards[board].info(); + + this.clear(); + if (info.name) { + BUG.info('Expansion.config() Added \'%s\' into slot %d', info.name, board + 1); + + this.write(0x00, info.type); + this.write(0x08, info.flags); + + this.write(0x04, info.product); + this.write(0x10, info.vendor >> 8); + this.write(0x14, info.vendor & 0x0f); + + this.write(0x18, (info.serial >> 24) & 0xff); + this.write(0x1c, (info.serial >> 16) & 0xff); + this.write(0x20, (info.serial >> 8) & 0xff); + this.write(0x24, info.serial & 0xff); + + this.write(0x28, (info.rom >> 8) & 0xff); + this.write(0x2c, info.rom & 0xff); + + this.write(0x40, info.ctrl); + } + } +} \ No newline at end of file diff --git a/sae/input.js b/sae/input.js new file mode 100644 index 0000000..43c38c2 --- /dev/null +++ b/sae/input.js @@ -0,0 +1,801 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Mouse() { + this.button = [false, false, false]; + this.pos = 0; + + var cx = 0; + var cy = 0; + var mx = 0; + var my = 0; + var lx = -1; + var ly = -1; + + this.reset = function() { + this.button = [0, 0, 0]; + cx = cy = 0; + lx = ly = -1; + } + + this.mousedown = function (e) { + e = e || window.event; + if (!e) return; + + this.button[e.button] = true; + } + + this.mouseup = function (e) { + e = e || window.event; + if (!e) return; + + this.button[e.button] = false; + } + + this.mouseover = function (e) { + //AMIGA.video.hideCursor(1); + this.mousemove(e); + lx = cx; + ly = cy; + } + + this.mouseout = function (e) { + //AMIGA.video.hideCursor(0); + this.mouseup(e); + } + + this.mousemove = function (e) { + e = e || window.event; + if (!e || !AMIGA.video) return; + + if (e.pageX || e.pageY) { + cx = e.pageX; + cy = e.pageY; + } else if (e.clientX || e.clientY) { + cx = e.clientX; + cy = e.clientY; + } + if (lx == -1) { + lx = cx; + ly = cy; + } + //BUG.info('USER() mousemove %d %d', cx, cy); + } + + this.update = function() { + if (lx != -1) { + var dx = cx - lx; + var dy = cy - ly; + + if (dx > 127) dx = 127; else if (dx < -127) dx = -127; + mx += dx; + if (mx > 255) mx -= 256; else if (mx < 0) mx += 256; + + if (dy > 127) dy = 127; else if (dy < -127) dy = -127; + my += dy; + if (my > 255) my -= 256; else if (my < 0) my += 256; + } else + mx = my = 0; + + lx = cx; + ly = cy; + + this.pos = (my << 8) + mx; + //BUG.info('USER() mousemove %d %d, ps $%04x', mx, my, this.pos); + } +} + +function Joystick(type) { + this.type = type; + this.button = [false, false, false]; + this.state = [false, false, false, false]; + this.dir = 0; + var x = 0; + var y = 0; + + this.reset = function() { + this.button = [false, false, false]; + this.state = [false, false, false, false]; + } + + this.update = function() { + var u = this.state[1]; + var d = this.state[3]; + + if (this.state[0]) u = !u; + if (this.state[2]) d = !d; + + this.dir = d | (this.state[2] << 1) | (u << 8) | (this.state[0] << 9); + + /*var l = 1, r = 1, u = 1, d = 1; + + if (this.state[0]) l = 0; + if (this.state[1]) u = 0; + if (this.state[2]) r = 0; + if (this.state[3]) d = 0; + + var b0 = (d ^ r) ? 1 : 0; + var b1 = (r ^ 1) ? 2 : 0; + var b8 = (u ^ l) ? 1 : 0; + var b9 = (l ^ 1) ? 2 : 0; + + this.dir = ((b8 | b9) << 8) | (b0 | b1);*/ + } +} + +function Keyboard() { + const RAWKEY_TILDE = 0x00; + const RAWKEY_1 = 0x01; + const RAWKEY_2 = 0x02; + const RAWKEY_3 = 0x03; + const RAWKEY_4 = 0x04; + const RAWKEY_5 = 0x05; + const RAWKEY_6 = 0x06; + const RAWKEY_7 = 0x07; + const RAWKEY_8 = 0x08; + const RAWKEY_9 = 0x09; + const RAWKEY_0 = 0x0A; + const RAWKEY_MINUS = 0x0B; + const RAWKEY_EQUAL = 0x0C; + const RAWKEY_BACKSLASH = 0x0D; + const RAWKEY_KP_0 = 0x0F; + const RAWKEY_Q = 0x10; + const RAWKEY_W = 0x11; + const RAWKEY_E = 0x12; + const RAWKEY_R = 0x13; + const RAWKEY_T = 0x14; + const RAWKEY_Y = 0x15; + const RAWKEY_U = 0x16; + const RAWKEY_I = 0x17; + const RAWKEY_O = 0x18; + const RAWKEY_P = 0x19; + const RAWKEY_LBRACKET = 0x1A; + const RAWKEY_RBRACKET = 0x1B; + const RAWKEY_KP_1 = 0x1D; + const RAWKEY_KP_2 = 0x1E; + const RAWKEY_KP_3 = 0x1F; + const RAWKEY_A = 0x20; + const RAWKEY_S = 0x21; + const RAWKEY_D = 0x22; + const RAWKEY_F = 0x23; + const RAWKEY_G = 0x24; + const RAWKEY_H = 0x25; + const RAWKEY_J = 0x26; + const RAWKEY_K = 0x27; + const RAWKEY_L = 0x28; + const RAWKEY_SEMICOLON = 0x29; + const RAWKEY_QUOTE = 0x2A; + const RAWKEY_2B = 0x2B; + const RAWKEY_KP_4 = 0x2D; + const RAWKEY_KP_5 = 0x2E; + const RAWKEY_KP_6 = 0x2F; + const RAWKEY_LESSGREATER = 0x30; + const RAWKEY_Z = 0x31; + const RAWKEY_X = 0x32; + const RAWKEY_C = 0x33; + const RAWKEY_V = 0x34; + const RAWKEY_B = 0x35; + const RAWKEY_N = 0x36; + const RAWKEY_M = 0x37; + const RAWKEY_COMMA = 0x38; + const RAWKEY_PERIOD = 0x39; + const RAWKEY_SLASH = 0x3A; + const RAWKEY_KP_DECIMAL = 0x3C; + const RAWKEY_KP_7 = 0x3D; + const RAWKEY_KP_8 = 0x3E; + const RAWKEY_KP_9 = 0x3F; + const RAWKEY_SPACE = 0x40; + const RAWKEY_BACKSPACE = 0x41; + const RAWKEY_TAB = 0x42; + const RAWKEY_KP_ENTER = 0x43; + const RAWKEY_RETURN = 0x44; + const RAWKEY_ESCAPE = 0x45; + const RAWKEY_DELETE = 0x46; + //const RAWKEY_INSERT = 0x47; + //const RAWKEY_PAGEUP = 0x48; + //const RAWKEY_PAGEDOWN = 0x49; + const RAWKEY_KP_MINUS = 0x4A; + //const RAWKEY_F11 = 0x4B; + const RAWKEY_UP = 0x4C; + const RAWKEY_DOWN = 0x4D; + const RAWKEY_RIGHT = 0x4E; + const RAWKEY_LEFT = 0x4F; + const RAWKEY_F1 = 0x50; + const RAWKEY_F2 = 0x51; + const RAWKEY_F3 = 0x52; + const RAWKEY_F4 = 0x53; + const RAWKEY_F5 = 0x54; + const RAWKEY_F6 = 0x55; + const RAWKEY_F7 = 0x56; + const RAWKEY_F8 = 0x57; + const RAWKEY_F9 = 0x58; + const RAWKEY_F10 = 0x59; + const RAWKEY_KP_DIVIDE = 0x5C; + const RAWKEY_KP_MULTIPLY = 0x5D; + const RAWKEY_KP_PLUS = 0x5E; + const RAWKEY_HELP = 0x5F; + const RAWKEY_LSHIFT = 0x60; + const RAWKEY_RSHIFT = 0x61; + const RAWKEY_CAPSLOCK = 0x62; + const RAWKEY_CONTROL = 0x63; + const RAWKEY_LALT = 0x64; + const RAWKEY_RALT = 0x65; + const RAWKEY_LAMIGA = 0x66; + const RAWKEY_RAMIGA = 0x67; + /*const RAWKEY_SCRLOCK = 0x6B; + const RAWKEY_PRTSCREEN = 0x6C; + const RAWKEY_NUMLOCK = 0x6D; + const RAWKEY_PAUSE = 0x6E; + const RAWKEY_F12 = 0x6F; + const RAWKEY_HOME = 0x70; + const RAWKEY_END = 0x71; + const RAWKEY_MEDIA1 = 0x72; + const RAWKEY_MEDIA2 = 0x73; + const RAWKEY_MEDIA3 = 0x74; + const RAWKEY_MEDIA4 = 0x75; + const RAWKEY_MEDIA5 = 0x76; + const RAWKEY_MEDIA6 = 0x77; + const RAWKEY_NM_WHEEL_UP = 0x7A; + const RAWKEY_NM_WHEEL_DOWN = 0x7B; + const RAWKEY_NM_WHEEL_LEFT = 0x7C; + const RAWKEY_NM_WHEEL_RIGHT = 0x7D; + const RAWKEY_NM_BUTTON_FOURTH = 0x7E;*/ + const RAWKEY_BAD_CODE = 0xF9; + const RAWKEY_BUFFER_OVERFLOW = 0xFA; + const RAWKEY_SELFTEST_FAILED = 0xFC; + const RAWKEY_INIT_POWER_UP = 0xFD; + const RAWKEY_TERM_POWER_UP = 0xFE; + + const defKeyCodeMap = { + 8:RAWKEY_BACKSPACE, //backspace + 9:RAWKEY_TAB, //tab + 13:RAWKEY_RETURN, //enter + 16:RAWKEY_LSHIFT, //shift + 17:RAWKEY_CONTROL, //ctrl + 18:RAWKEY_LALT, //alt + //19:RAWKEY_PAUSE, //pause/break + 20:RAWKEY_CAPSLOCK, //caps lock + 27:RAWKEY_ESCAPE, //escape + 32:RAWKEY_SPACE, //space + //33:RAWKEY_PAGEUP, //page up + //34:RAWKEY_PAGEDOWN, //page down + //35:RAWKEY_END, //end + //36:RAWKEY_HOME, //home + 37:RAWKEY_LEFT, //left arrow + 38:RAWKEY_UP, //up arrow + 39:RAWKEY_RIGHT, //right arrow + 40:RAWKEY_DOWN, //down arrow + //45:RAWKEY_INSERT, //insert + 46:RAWKEY_DELETE, //delete + 48:RAWKEY_0, //0 + 49:RAWKEY_1, //1 + 50:RAWKEY_2, //2 + 51:RAWKEY_3, //3 + 52:RAWKEY_4, //4 + 53:RAWKEY_5, //5 + 54:RAWKEY_6, //6 + 55:RAWKEY_7, //7 + 56:RAWKEY_8, //8 + 57:RAWKEY_9, //9 + 65:RAWKEY_A, //a + 66:RAWKEY_B, //b + 67:RAWKEY_C, //c + 68:RAWKEY_D, //d + 69:RAWKEY_E, //e + 70:RAWKEY_F, //f + 71:RAWKEY_G, //g + 72:RAWKEY_H, //h + 73:RAWKEY_I, //i + 74:RAWKEY_J, //j + 75:RAWKEY_K, //k + 76:RAWKEY_L, //l + 77:RAWKEY_M, //m + 78:RAWKEY_N, //n + 79:RAWKEY_O, //o + 80:RAWKEY_P, //p + 81:RAWKEY_Q, //q + 82:RAWKEY_R, //r + 83:RAWKEY_S, //s + 84:RAWKEY_T, //t + 85:RAWKEY_U, //u + 86:RAWKEY_V, //v + 87:RAWKEY_W, //w + 88:RAWKEY_X, //x + 89:RAWKEY_Z, //y + 90:RAWKEY_Y, //z + 91:RAWKEY_LAMIGA, //left window key + 92:RAWKEY_RAMIGA, //right window key + 93:RAWKEY_HELP, //select key + 96:RAWKEY_KP_0, //numpad 0 + 97:RAWKEY_KP_1, //numpad 1 + 98:RAWKEY_KP_2, //numpad 2 + 99:RAWKEY_KP_3, //numpad 3 + 100:RAWKEY_KP_4, //numpad 4 + 101:RAWKEY_KP_5, //numpad 5 + 102:RAWKEY_KP_6, //numpad 6 + 103:RAWKEY_KP_7, //numpad 7 + 104:RAWKEY_KP_8, //numpad 8 + 105:RAWKEY_KP_9, //numpad 9 + 106:RAWKEY_KP_MULTIPLY, //multiply + 107:RAWKEY_KP_PLUS, //add + 109:RAWKEY_KP_MINUS, //subtract + 110:RAWKEY_KP_DECIMAL, //decimal point + 111:RAWKEY_KP_DIVIDE, //divide + 112:RAWKEY_F1 , //f1 + 113:RAWKEY_F2 , //f2 + 114:RAWKEY_F3 , //f3 + 115:RAWKEY_F4 , //f4 + 116:RAWKEY_F5 , //f5 + 117:RAWKEY_F6 , //f6 + 118:RAWKEY_F7 , //f7 + 119:RAWKEY_F8 , //f8 + 120:RAWKEY_F9 , //f9 + 121:RAWKEY_F10, //f10 + //122:RAWKEY_F11, //f11 + //123:RAWKEY_F12, //f12 + //144:RAWKEY_NUMLOCK, //num lock + //145:RAWKEY_SCRLOCK, //scroll lock + /*186:RAWKEY_SEMICOLON, //semi-colon + 187:RAWKEY_EQUAL, //equal sign + 188:RAWKEY_COMMA, //comma + 189:RAWKEY_MINUS, //dash + 190:RAWKEY_PERIOD, //period + 191:RAWKEY_SLASH, //forward slash + 192:RAWKEY_TILDE, //grave accent + 219:RAWKEY_LBRACKET, //open bracket + 220:RAWKEY_BACKSLASH, //back slash + 221:RAWKEY_RBRACKET, //close braket + 222:RAWKEY_QUOTE, //single quote + 226:RAWKEY_LESSGREATER*/ + 186:RAWKEY_LBRACKET, + 187:RAWKEY_RBRACKET, + 188:RAWKEY_COMMA, + 189:RAWKEY_SLASH, + 190:RAWKEY_PERIOD, + 191:RAWKEY_2B, + 192:RAWKEY_SEMICOLON, + 219:RAWKEY_MINUS, + 220:RAWKEY_TILDE, + 221:RAWKEY_EQUAL, + 222:RAWKEY_QUOTE, + 226:RAWKEY_LESSGREATER + }; + const mozKeyCodeMap = { + 8:RAWKEY_BACKSPACE, //backspace + 9:RAWKEY_TAB, //tab + 13:RAWKEY_RETURN, //enter + 16:RAWKEY_LSHIFT, //shift + 17:RAWKEY_CONTROL, //ctrl + 18:RAWKEY_LALT, //alt + //19:RAWKEY_PAUSE, //pause/break + 20:RAWKEY_CAPSLOCK, //caps lock + 27:RAWKEY_ESCAPE, //escape + 32:RAWKEY_SPACE, //space + //33:RAWKEY_PAGEUP, //page up + //34:RAWKEY_PAGEDOWN, //page down + //35:RAWKEY_END, //end + //36:RAWKEY_HOME, //home + 37:RAWKEY_LEFT, //left arrow + 38:RAWKEY_UP, //up arrow + 39:RAWKEY_RIGHT, //right arrow + 40:RAWKEY_DOWN, //down arrow + //45:RAWKEY_INSERT, //insert + 46:RAWKEY_DELETE, //delete + 48:RAWKEY_0, //0 + 49:RAWKEY_1, //1 + 50:RAWKEY_2, //2 + 51:RAWKEY_3, //3 + 52:RAWKEY_4, //4 + 53:RAWKEY_5, //5 + 54:RAWKEY_6, //6 + 55:RAWKEY_7, //7 + 56:RAWKEY_8, //8 + 57:RAWKEY_9, //9 + 60:RAWKEY_LESSGREATER, + 63:RAWKEY_MINUS, + 65:RAWKEY_A, //a + 66:RAWKEY_B, //b + 67:RAWKEY_C, //c + 68:RAWKEY_D, //d + 69:RAWKEY_E, //e + 70:RAWKEY_F, //f + 71:RAWKEY_G, //g + 72:RAWKEY_H, //h + 73:RAWKEY_I, //i + 74:RAWKEY_J, //j + 75:RAWKEY_K, //k + 76:RAWKEY_L, //l + 77:RAWKEY_M, //m + 78:RAWKEY_N, //n + 79:RAWKEY_O, //o + 80:RAWKEY_P, //p + 81:RAWKEY_Q, //q + 82:RAWKEY_R, //r + 83:RAWKEY_S, //s + 84:RAWKEY_T, //t + 85:RAWKEY_U, //u + 86:RAWKEY_V, //v + 87:RAWKEY_W, //w + 88:RAWKEY_X, //x + 89:RAWKEY_Z, //y + 90:RAWKEY_Y, //z + 91:RAWKEY_LAMIGA, //left window key + 92:RAWKEY_RAMIGA, //right window key + 93:RAWKEY_HELP, //select key + 96:RAWKEY_KP_0, //numpad 0 + 97:RAWKEY_KP_1, //numpad 1 + 98:RAWKEY_KP_2, //numpad 2 + 99:RAWKEY_KP_3, //numpad 3 + 100:RAWKEY_KP_4, //numpad 4 + 101:RAWKEY_KP_5, //numpad 5 + 102:RAWKEY_KP_6, //numpad 6 + 103:RAWKEY_KP_7, //numpad 7 + 104:RAWKEY_KP_8, //numpad 8 + 105:RAWKEY_KP_9, //numpad 9 + 106:RAWKEY_KP_MULTIPLY, //multiply + 107:RAWKEY_KP_PLUS, //add + 109:RAWKEY_KP_MINUS, //subtract + 110:RAWKEY_KP_DECIMAL, //decimal point + 111:RAWKEY_KP_DIVIDE, //divide + 112:RAWKEY_F1 , //f1 + 113:RAWKEY_F2 , //f2 + 114:RAWKEY_F3 , //f3 + 115:RAWKEY_F4 , //f4 + 116:RAWKEY_F5 , //f5 + 117:RAWKEY_F6 , //f6 + 118:RAWKEY_F7 , //f7 + 119:RAWKEY_F8 , //f8 + 120:RAWKEY_F9 , //f9 + 121:RAWKEY_F10, //f10 + //122:RAWKEY_F11, //f11 + //123:RAWKEY_F12, //f12 + //144:RAWKEY_NUMLOCK, //num lock + //145:RAWKEY_SCRLOCK, //scroll lock + 160:RAWKEY_TILDE, + 163:RAWKEY_2B, + 171:RAWKEY_RBRACKET, + 173:RAWKEY_SLASH, + 188:RAWKEY_COMMA, + 190:RAWKEY_PERIOD, + 192:RAWKEY_EQUAL + }; + const MAXKEYS = 256; + const KEYBUFSIZE = 512; + + var keyState = new Uint8Array(MAXKEYS); + var keyBuf = new Uint8Array(KEYBUFSIZE); + var state = 0; + var code = 0; + var first = 0, last = 0; + var capsLock = false; + + var hsynccnt = 0; + this.lostsynccnt = 0; + + //for (var k in KeyEvent) document.writeln('KeyEvent.' + k + ' = ' + KeyEvent[k]+'
'); //FF + for (var i = 0; i < MAXKEYS; i++) keyState[i] = false; + for (var i = 0; i < KEYBUFSIZE; i++) keyBuf[i] = 0; + + function _onkeydown(e) { AMIGA.input.keyboard.handleKey(e, true); } + function _onkeyup(e) { AMIGA.input.keyboard.handleKey(e, false); } + + this.setup = function() { + /*document.onkeydown = function (e) { + AMIGA.input.keyboard.keydownup(e, true); + } + document.onkeyup = function (e) { + AMIGA.input.keyboard.keydownup(e, false); + }*/ + window.document.addEventListener('keydown', _onkeydown, false); + window.document.addEventListener('keyup', _onkeyup, false); + } + + this.cleanup = function() { + //BUG.info('Keyboard.cleanup()'); + //document.onkeydown = null; + //document.onkeyup = null; + window.document.removeEventListener('keydown', _onkeydown, false); + window.document.removeEventListener('keyup', _onkeyup, false); + } + + this.reset = function() { + for (var i = 0; i < MAXKEYS; i++) keyState[i] = false; + state = 0; + code = 0; + first = last = 0; + hsynccnt = 0; + this.lostsynccnt = 0; + } + + this.keysAvail = function() { + return first != last; + } + + this.nextKey = function() { + //assert (first != last); + var key = keyBuf[last]; + if (++last == KEYBUFSIZE) last = 0; + return key; + } + + this.recordKey = function (kc) { + var next = first + 1; + + if (next == KEYBUFSIZE) next = 0; + if (next == last) { + BUG.info('Keyboard() buffer overrun!'); + return false; + } + keyBuf[first] = kc; + first = next; + return true; + } + + this.processKey = function (code, down) { + /* Caps-lock */ + if (code == 20) { + if (down) { + capsLock = !capsLock; + if (!capsLock) return; + } else { + if (capsLock) return; + } + } + + /* joystick emul */ + if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Joy0) { + var l,u,r,d,f1,f2; + switch (AMIGA.config.ports[0].move) { + case SAEV_Config_Ports_Move_Arrows: { l = 37; u = 38; r = 39; d = 40; break; } + case SAEV_Config_Ports_Move_Numpad: { l = 100; u = 104; r = 102; d = 101; break; } + case SAEV_Config_Ports_Move_WASD: { l = 65; u = 87; r = 68; d = 83; break; } + } + f1 = AMIGA.config.ports[0].fire[0]; + f2 = AMIGA.config.ports[0].fire[1]; + switch (code) { + case f1: { AMIGA.input.joystick[0].button[0] = down; break; } + case f2: { AMIGA.input.joystick[0].button[1] = down; break; } + case l: { AMIGA.input.joystick[0].state[0] = down; if (down && AMIGA.input.joystick[0].state[2]) AMIGA.input.joystick[0].state[2] = false; break; } + case u: { AMIGA.input.joystick[0].state[1] = down; if (down && AMIGA.input.joystick[0].state[3]) AMIGA.input.joystick[0].state[3] = false; break; } + case r: { AMIGA.input.joystick[0].state[2] = down; if (down && AMIGA.input.joystick[0].state[0]) AMIGA.input.joystick[0].state[0] = false; break; } + case d: { AMIGA.input.joystick[0].state[3] = down; if (down && AMIGA.input.joystick[0].state[1]) AMIGA.input.joystick[0].state[1] = false; break; } + } + } + if (AMIGA.config.ports[1].type == SAEV_Config_Ports_Type_Joy1) { + var l,u,r,d,f1,f2; + switch (AMIGA.config.ports[1].move) { + case SAEV_Config_Ports_Move_Arrows: { l = 37; u = 38; r = 39; d = 40; break; } + case SAEV_Config_Ports_Move_Numpad: { l = 100; u = 104; r = 102; d = 101; break; } + case SAEV_Config_Ports_Move_WASD: { l = 65; u = 87; r = 68; d = 83; break; } + } + f1 = AMIGA.config.ports[1].fire[0]; + f2 = AMIGA.config.ports[1].fire[1]; + switch (code) { + case f1: { AMIGA.input.joystick[1].button[0] = down; break; } + case f2: { AMIGA.input.joystick[1].button[1] = down; break; } + case l: { AMIGA.input.joystick[1].state[0] = down; if (down && AMIGA.input.joystick[1].state[2]) AMIGA.input.joystick[1].state[2] = false; break; } + case u: { AMIGA.input.joystick[1].state[1] = down; if (down && AMIGA.input.joystick[1].state[3]) AMIGA.input.joystick[1].state[3] = false; break; } + case r: { AMIGA.input.joystick[1].state[2] = down; if (down && AMIGA.input.joystick[1].state[0]) AMIGA.input.joystick[1].state[0] = false; break; } + case d: { AMIGA.input.joystick[1].state[3] = down; if (down && AMIGA.input.joystick[1].state[1]) AMIGA.input.joystick[1].state[1] = false; break; } + } + } + + if (!AMIGA.config.keyboard.enabled) + return; + + /* map shift-keys (team17 pinball games) */ + if (AMIGA.config.keyboard.mapShift) { + switch (code) { + case 37: { //left arrow + var rawkey = RAWKEY_LSHIFT; + if (!down) { + this.recordKey((rawkey << 1) | 1); + } else { + this.recordKey(rawkey << 1); + } + //break; + return; + } + case 39: { //right arrow + var rawkey = RAWKEY_RSHIFT; + if (!down) { + this.recordKey((rawkey << 1) | 1); + } else { + this.recordKey(rawkey << 1); + } + //break; + return; + } + } + } + + var rawkey = false; + if (BrowserDetect.browser == 'Firefox') { + if (typeof(mozKeyCodeMap[code]) != 'undefined') + rawkey = mozKeyCodeMap[code]; + } else { + if (typeof(defKeyCodeMap[code]) != 'undefined') + rawkey = defKeyCodeMap[code]; + } + //BUG.info('Keyboard.processKey() code %d $%04x, rawkey $%04x', code, code, rawkey); + + if (rawkey !== false) { + if (down) + this.recordKey(rawkey << 1); + else + this.recordKey((rawkey << 1) | 1); + } + } + + this.handleKey = function (e, down) { + e = e || window.event; + var code = e.which ? e.which : e.keyCode; + + if (AMIGA.config.keyboard.enabled && code != 122 && code != 123) //all but F11 F12 + e.preventDefault(); + + //BUG.info('Keyboard.handleKey() down %d, code %d, alt %d, shift %d, ctrl %d', down?1:0, code, e.altKey?1:0, e.shiftKey?1:0, e.ctrlKey?1:0); + + /* Ctrl-Alt fix */ + if (!down && code == 17 && keyState[18]) { + keyState[18] = false; + this.processKey(18, keyState[18]); + } + + var oldstate = keyState[code]; + if (down && !keyState[code]) { + keyState[code] = true; + } + else if (!down) { + keyState[code] = false; + } + if (keyState[code] != oldstate) { + this.processKey(code, keyState[code]); + } + } + + this.setCode = function (keycode) { + code = ~((keycode << 1) | (keycode >> 7)) & 0xff; + } + + this.keyReq = function() { + this.lostsynccnt = 8 * AMIGA.events.maxvpos * 8; /* 8 frames * 8 bits */ + + AMIGA.cia.setICR(CIA_A, 8, code); + } + + this.hsync = function() { + if ((this.keysAvail() || state < 3) && !this.lostsynccnt && ((++hsynccnt) & 15) == 0) { + switch (state) { + case 0: + code = 0; + state++; + break; + case 1: + this.setCode(RAWKEY_INIT_POWER_UP); + state++; + break; + case 2: + this.setCode(RAWKEY_TERM_POWER_UP); + state++; + break; + case 3: + code = ~this.nextKey() & 0xff; + break; + } + this.keyReq(); + } + } + + this.vsync = function() { + if (this.lostsynccnt > 0) { + this.lostsynccnt -= AMIGA.events.maxvpos; + if (this.lostsynccnt <= 0) { + this.lostsynccnt = 0; + this.keyReq(); + //BUG.info('Keyboard() lost sync'); + } + } + } +} + +function Input() { + this.mouse = new Mouse(); + this.joystick = new Array(2); + this.joystick[0] = new Joystick(SAEV_Config_Ports_Type_Joy0); + this.joystick[1] = new Joystick(SAEV_Config_Ports_Type_Joy1); + this.keyboard = new Keyboard(); + + var potgo = { + data: 0, + count: 0 + }; + + this.setup = function() { + this.keyboard.setup(); + } + + this.cleanup = function() { + this.keyboard.cleanup(); + } + + this.reset = function() { + this.mouse.reset(); + this.joystick[0].reset(); + this.joystick[1].reset(); + this.keyboard.reset(); + potgo.data = 0; + potgo.count = 0; + } + + this.POTGO = function (v) { + //BUG.info('Input.POTGO() $%04x', v); + potgo.data = v; + } + + this.POTGOR = function() { + var v = (potgo.data | (potgo.data << 1)) & 0xaa00; + v |= v >> 1; + + if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Mouse) { + if (this.mouse.button[2]) v &= 0xfbff; + if (this.mouse.button[1]) v &= 0xfeff; + } else if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Joy0) { + if (this.joystick[0].button[1]) v &= 0xfbff; + if (this.joystick[0].button[2]) v &= 0xfeff; + } + if (AMIGA.config.ports[1].type == SAEV_Config_Ports_Type_Joy1) { + if (this.joystick[1].button[1]) v &= 0xbfff; + if (this.joystick[1].button[2]) v &= 0xefff; + } + //BUG.info('Input.POTGOR() $%04x', v); + return v; + } + + this.POT0DAT = function() { + if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Mouse) { + if (this.mouse.button[2]) potgo.count = (potgo.count & 0xff00) | ((potgo.count + 1) & 0xff); + if (this.mouse.button[1]) potgo.count = (potgo.count + 0x100) & 0xffff; + } + //BUG.info('Input.POT0DAT() $%04x', v); + return potgo.count; + } + + this.POT1DAT = function() { + //BUG.info('Input.POT1DAT() NOT IMPLEMENTED'); + return 0xffff; + } + + this.JOY0DAT = function() { + if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Mouse) { + this.mouse.update(); + return this.mouse.pos; + } else if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Joy0) { + this.joystick[0].update(); + return this.joystick[0].dir; + } + return 0xffff; + } + + this.JOY1DAT = function() { + if (AMIGA.config.ports[1].type == SAEV_Config_Ports_Type_Mouse) { + this.mouse.update(); + return this.mouse.pos; + } else if (AMIGA.config.ports[1].type == SAEV_Config_Ports_Type_Joy1) { + this.joystick[1].update(); + return this.joystick[1].dir; + } + return 0xffff; + } + + this.JOYTEST = function (v) { + //BUG.info('Input.JOYTEST() $%04x', v); + } +} \ No newline at end of file diff --git a/sae/memory.js b/sae/memory.js new file mode 100644 index 0000000..6afc7fb --- /dev/null +++ b/sae/memory.js @@ -0,0 +1,652 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ +/* +0x0000 0000 2024.0 Chip RAM +0x00C0 0000 1536.0 Slow RAM + +0x00F8 0000 256.0 256K System ROM (Kickstart 2.04 or higher) +0x00FC 0000 256.0 256K System ROM + +0x00DF F000 4.0 Custom chip registers +0x00BF D000 3.8 8520-B (even-byte addresses) +0x00BF E001 3.8 8520-A (odd-byte addresses) +0x00DC 0000 64.0 Real time clock + +0x00F0 0000 512.0 Reserved 512K System ROM (CDTV or CD³²) +0x00E0 0000 512.0 Reserved +0x00A0 0000 1984.0 Reserved +0x00D8 0000 256.0 Reserved +0x00DD 0000 188.0 Reserved + +0x0020 0000 8192.0 Primary auto-config space (Fast RAM) +0x00E8 0000 64.0 Zorro II auto-config space (before relocation) +0x00E9 0000 448.0 Secondary auto-config space (usually 64K I/O boards) +*/ + +function Memory() { + const NULL8 = 0xff; + const NULL16 = 0xffff; + const NULL32 = 0xffffffff; + + this.chip = { + size: 0, + align: 0, + data: null, + lower: 0, + upper: 0, + }; + this.slow = { + enabled: false, + size: 0, + align: 0, + data: null, + lower: 0x00C00000, + upper: 0 + }; + this.fast = { + enabled: false, + size: 0, + align: 0, + data: null, + lower: 0x00200000, + upper: 0 + }; + this.rom = { + size: 0, + align: 0, + data: null, + lower: 0xf80000, + upper: 0x1000000 + }; + this.res_d8 = { + size: 0x40000, + align: 0x20000, + data: null, + lower: 0x00D80000, + upper: 0x00DC0000 + }; + this.res_e0 = { + size: 0x80000, + align: 0x40000, + data: null, + lower: 0x00E00000, + upper: 0x00E80000 + }; + this.res_f0 = { + size: 0x80000, + align: 0x40000, + data: null, + lower: 0x00F00000, + upper: 0x00F80000 + }; + this.ac_z2 = { + size: 0x10000, + lower: 0x00E80000, + upper: 0x00E90000 + }; + this.aros = { + cached: false, + rom: '', + ext: '' + }; + + function getChipSize(v) { + switch (v) { + case SAEV_Config_RAM_Chip_Size_256K: return 256 << 10; + case SAEV_Config_RAM_Chip_Size_512K: return 512 << 10; + case SAEV_Config_RAM_Chip_Size_1M: return 1024 << 10; + case SAEV_Config_RAM_Chip_Size_2M: return 2048 << 10; + default: return false; + } + } + + function getSlowSize(v) { + switch (v) { + case SAEV_Config_RAM_Slow_Size_None: return 0; + case SAEV_Config_RAM_Slow_Size_256K: return 256 << 10; + case SAEV_Config_RAM_Slow_Size_512K: return 512 << 10; + case SAEV_Config_RAM_Slow_Size_1M: return 1024 << 10; + case SAEV_Config_RAM_Slow_Size_1536K: return 1536 << 10; + default: return false; + } + } + + function getFastSize(v) { + switch (v) { + case SAEV_Config_RAM_Fast_Size_None: return 0; + case SAEV_Config_RAM_Fast_Size_512K: return 512 << 10; + case SAEV_Config_RAM_Fast_Size_1M: return 1024 << 10; + case SAEV_Config_RAM_Fast_Size_2M: return 2048 << 10; + case SAEV_Config_RAM_Fast_Size_4M: return 4096 << 10; + case SAEV_Config_RAM_Fast_Size_8M: return 8192 << 10; + default: return false; + } + } + + function getROMSize(v) { + switch (v) { + case SAEV_Config_ROM_Size_256K: return 256 << 10; + case SAEV_Config_ROM_Size_512K: return 512 << 10; + default: return false; + } + } + + /*function getEXTSize(v) { + switch (v) { + case SAEV_Config_EXT_Size_256K: return 256 << 10; + case SAEV_Config_EXT_Size_512K: return 512 << 10; + default: return false; + } + } + function getEXTAddr(v) { + switch (v) { + case SAEV_Config_EXT_Addr_A0: return 0xa00000; + case SAEV_Config_EXT_Addr_E0: return 0xe00000; + case SAEV_Config_EXT_Addr_F0: return 0xf00000; + default: return false; + } + }*/ + + this.setup = function() { + this.chip.size = getChipSize(AMIGA.config.ram.chip.size); + this.chip.align = this.chip.size >>> 1; + this.chip.data = new Uint16Array(this.chip.align); for (var i = 0; i < this.chip.align; i++) this.chip.data[i] = 0; + this.chip.lower = 0; + this.chip.upper = this.chip.size; + + if (AMIGA.config.ram.slow.size) { + this.slow.enabled = true; + this.slow.size = getSlowSize(AMIGA.config.ram.slow.size); + this.slow.align = this.slow.size >>> 1; + this.slow.data = new Uint16Array(this.slow.align); for (var i = 0; i < this.slow.align; i++) this.slow.data[i] = 0; + this.slow.upper = this.slow.lower + this.slow.size; + } else { + this.slow.enabled = false; + this.slow.size = 0; + this.slow.align = 0; + this.slow.data = null; + this.slow.upper = 0; + } + if (AMIGA.config.ram.fast.size) { + this.fast.enabled = true; + this.fast.size = getFastSize(AMIGA.config.ram.fast.size); + this.fast.align = this.fast.size >>> 1; + this.fast.data = new Uint16Array(this.fast.align); for (var i = 0; i < this.fast.align; i++) this.fast.data[i] = 0; + this.fast.upper = this.fast.lower + this.fast.size; + } else { + this.fast.enabled = false; + this.fast.size = 0; + this.fast.align = 0; + this.fast.data = null; + this.fast.upper = 0; + } + BUG.info('Memory.init() chip %d, slow %d, fast %d', this.chip.size >>> 10, this.slow.size >>> 10, this.fast.size >>> 10); + + this.rom.size = getROMSize(AMIGA.config.rom.size); + this.rom.align = this.rom.size >>> 1; + this.rom.data = new Uint16Array(this.rom.align); for (var i = 0; i < this.rom.align; i++) this.rom.data[i] = 0; + + this.res_d8.data = new Uint16Array(this.res_d8.align); for (var i = 0; i < this.res_d8.align; i++) this.res_d8.data[i] = 0; + this.res_e0.data = new Uint16Array(this.res_e0.align); for (var i = 0; i < this.res_e0.align; i++) this.res_e0.data[i] = 0; + this.res_f0.data = new Uint16Array(this.res_f0.align); for (var i = 0; i < this.res_f0.align; i++) this.res_f0.data[i] = 0; + + AMIGA.mem.copy_rom(AMIGA.config.rom.data); + + if (AMIGA.config.ext.size != SAEV_Config_EXT_Size_None) { + if (AMIGA.config.ext.addr == SAEV_Config_EXT_Addr_E0) + AMIGA.mem.copy_e0(AMIGA.config.ext.data); + else if (AMIGA.config.ext.addr == SAEV_Config_EXT_Addr_F0) + AMIGA.mem.copy_f0(AMIGA.config.ext.data); + } + + /*if (AMIGA.config.rom.mode == 1) { + if (!this.aros.cached) { + BUG.info('Memory.setup() AROS-ROM is not cached, downloading...'); + AMIGA.loading += 2; + loadRemote('aros-amiga-m68k-rom.bin', 0xfc4635e1, function(data) { + AMIGA.mem.aros.cached = true; + AMIGA.mem.aros.rom = data; + AMIGA.mem.copy_rom(data); + AMIGA.loading--; + }); + loadRemote('aros-amiga-m68k-ext.bin', 0xc612f82e, function(data) { + AMIGA.mem.aros.cached = true; + AMIGA.mem.aros.ext = data; + AMIGA.mem.copy_e0(data); + AMIGA.loading--; + }); + } else { + BUG.info('Memory.setup() AROS-ROM is cached, download skipped.'); + this.copy_rom(this.aros.rom); + this.copy_e0(this.aros.ext); + } + } else { + AMIGA.loading++; + loadLocal('cfg_rom_name', function(event) { + AMIGA.mem.copy_rom(event.target.result); + AMIGA.loading--; + }); + if (AMIGA.config.ext.size > 0) { + AMIGA.loading++; + loadLocal('cfg_ext_name', function(event) { + if (AMIGA.config.ext.addr == 0xe00000) + AMIGA.mem.copy_e0(event.target.result); + else + AMIGA.mem.copy_f0(event.target.result); + + AMIGA.loading--; + }); + } + }*/ + } + + this.load8 = function (addr) { + //BUG.info('Memory.load8() addr $%08x', addr); + + if (addr >= 0x000000 && addr < this.chip.size) { + return (addr & 1) ? (this.chip.data[addr >>> 1] & 0xff) : (this.chip.data[addr >>> 1] >> 8); + } + else if (this.slow.enabled && addr >= this.slow.lower && addr < this.slow.upper) { + return (addr & 1) ? (this.slow.data[(addr - this.slow.lower) >>> 1] & 0xff) : (this.slow.data[(addr - this.slow.lower) >>> 1] >> 8); + } + else if (this.fast.enabled && addr >= this.fast.lower && addr < this.fast.upper) { + return (addr & 1) ? (this.fast.data[(addr - this.fast.lower) >>> 1] & 0xff) : (this.fast.data[(addr - this.fast.lower) >>> 1] >> 8); + } + else if (addr >= this.rom.lower && addr < this.rom.upper) { + return (addr & 1) ? (this.rom.data[(addr - this.rom.lower) >>> 1] & 0xff) : (this.rom.data[(addr - this.rom.lower) >>> 1] >> 8); + } + else if (addr >= 0xdff000 && addr < 0xe00000) { + return AMIGA.custom.load8(addr); + } + else if (addr >= 0xbfd000 && addr < 0xbfdf01) { + return AMIGA.cia.load8(addr); + } + else if (addr >= 0xbfe001 && addr < 0xbfef02) { + return AMIGA.cia.load8(addr); + } + else if (addr >= 0xdc0000 && addr < 0xdd0000) { + return AMIGA.rtc.load8(addr); + } + else if (addr >= this.res_e0.lower && addr < this.res_e0.upper) { + return (addr & 1) ? (this.res_e0.data[(addr - this.res_e0.lower) >>> 1] & 0xff) : (this.res_e0.data[(addr - this.res_e0.lower) >>> 1] >> 8); + } + else if (addr >= this.res_f0.lower && addr < this.res_f0.upper) { + return (addr & 1) ? (this.res_f0.data[(addr - this.res_f0.lower) >>> 1] & 0xff) : (this.res_f0.data[(addr - this.res_f0.lower) >>> 1] >> 8); + } + else if (addr >= this.res_d8.lower && addr < this.res_d8.upper) { + return (addr & 1) ? (this.res_d0.data[(addr - this.res_d0.lower) >>> 1] & 0xff) : (this.res_d0.data[(addr - this.res_d0.lower) >>> 1] >> 8); + } + else if (addr >= this.ac_z2.lower && addr < this.ac_z2.upper) { + return AMIGA.expansion.load8(addr); + } + //else BUG.info('Memory.load8() ILLEGAL MEMORY ACCESS addr $%08x', addr); + + return NULL8; + } + + this.load16 = function (addr) { + //BUG.info('Memory.load16() addr $%08x', addr); + + if (addr >= 0 && addr < this.chip.size - 1) { + return this.chip.data[addr >>> 1]; + } + else if (this.slow.enabled && addr >= this.slow.lower && addr < this.slow.upper - 1) { + return this.slow.data[(addr - this.slow.lower) >>> 1]; + } + else if (this.fast.enabled && addr >= this.fast.lower && addr < this.fast.upper - 1) { + return this.fast.data[(addr - this.fast.lower) >>> 1]; + } + else if (addr >= this.rom.lower && addr < this.rom.upper - 1) { + return this.rom.data[(addr - this.rom.lower) >>> 1]; + } + else if (addr >= 0xdff000 && addr < 0xe00000 - 1) { + return AMIGA.custom.load16(addr); + } + else if (addr >= 0xbfd000 && addr < 0xbfdf01 - 1) { + return AMIGA.cia.load16(addr); + } + else if (addr >= 0xbfe001 && addr < 0xbfef02 - 1) { + return AMIGA.cia.load16(addr); + } + else if (addr >= 0xdc0000 && addr < 0xdd0000 - 1) { + return AMIGA.rtc.load16(addr); + } + else if (addr >= this.res_e0.lower && addr < this.res_e0.upper - 1) { + return this.res_e0.data[(addr - this.res_e0.lower) >>> 1]; + } + else if (addr >= this.res_f0.lower && addr < this.res_f0.upper - 1) { + return this.res_f0.data[(addr - this.res_f0.lower) >>> 1]; + } + else if (addr >= this.res_d8.lower && addr < this.res_d8.upper - 1) { + return this.res_d0.data[(addr - this.res_d0.lower) >>> 1]; + } + //else BUG.info('Memory.load16() ILLEGAL MEMORY ACCESS addr $%08x', addr); + + return NULL16; + } + + this.load32 = function (addr) { + //BUG.info('Memory.load32() addr $%08x', addr); + + if (addr >= 0 && addr < this.chip.size - 3) { + addr >>>= 1; + return ((this.chip.data[addr] << 16) | this.chip.data[addr + 1]) >>> 0; + } + else if (this.slow.enabled && addr >= this.slow.lower && addr < this.slow.upper - 3) { + addr = (addr - this.slow.lower) >>> 1; + return ((this.slow.data[addr] << 16) | this.slow.data[addr + 1]) >>> 0; + } + else if (this.fast.enabled && addr >= this.fast.lower && addr < this.fast.upper - 3) { + addr = (addr - this.fast.lower) >>> 1; + return ((this.fast.data[addr] << 16) | this.fast.data[addr + 1]) >>> 0; + } + else if (addr >= this.rom.lower && addr < this.rom.upper - 3) { + addr = (addr - this.rom.lower) >>> 1; + return ((this.rom.data[addr] << 16) | this.rom.data[addr + 1]) >>> 0; + } + else if (addr >= 0xdff000 && addr < 0xe00000 - 3) { + return AMIGA.custom.load32(addr); + } + else if (addr >= 0xbfd000 && addr < 0xbfdf01 - 3) { + return AMIGA.cia.load32(addr); + } + else if (addr >= 0xbfe001 && addr < 0xbfef02 - 3) { + return AMIGA.cia.load32(addr); + } + else if (addr >= 0xdc0000 && addr < 0xdd0000 - 3) { + return AMIGA.rtc.load32(addr); + } + else if (addr >= this.res_e0.lower && addr < this.res_e0.upper - 3) { + addr = (addr - this.res_e0.lower) >>> 1; + return ((this.res_e0.data[addr] << 16) | this.res_e0.data[addr + 1]) >>> 0; + } + else if (addr >= this.res_f0.lower && addr < this.res_f0.upper - 3) { + addr = (addr - this.res_f0.lower) >>> 1; + return ((this.res_f0.data[addr] << 16) | this.res_f0.data[addr + 1]) >>> 0; + } + else if (addr >= this.res_d8.lower && addr < this.res_d8.upper - 3) { + addr = (addr - this.res_d0.lower) >>> 1; + return ((this.res_d0.data[addr] << 16) | this.res_d0.data[addr + 1]) >>> 0; + } + //else BUG.info('Memory.load32() ILLEGAL MEMORY ACCESS addr $%08x', addr); + + return NULL32; + } + + this.store8 = function (addr, value) { + //BUG.info('Memory.store8() addr $%08x, val $%02x', addr, value); + + if (addr >= 0 && addr < this.chip.size) { + if (addr & 1) { + addr >>>= 1; + this.chip.data[addr] = (this.chip.data[addr] & 0xff00) | value; + } else { + addr >>>= 1; + this.chip.data[addr] = (value << 8) | (this.chip.data[addr] & 0x00ff); + } + } + else if (this.slow.enabled && addr >= this.slow.lower && addr < this.slow.upper) { + if (addr & 1) { + addr = (addr - this.slow.lower) >>> 1; + this.slow.data[addr] = (this.slow.data[addr] & 0xff00) | value; + } else { + addr = (addr - this.slow.lower) >>> 1; + this.slow.data[addr] = (value << 8) | (this.slow.data[addr] & 0x00ff); + } + } + else if (this.fast.enabled && addr >= this.fast.lower && addr < this.fast.upper) { + if (addr & 1) { + addr = (addr - this.fast.lower) >>> 1; + this.fast.data[addr] = (this.fast.data[addr] & 0xff00) | value; + } else { + addr = (addr - this.fast.lower) >>> 1; + this.fast.data[addr] = (value << 8) | (this.fast.data[addr] & 0x00ff); + } + } + else if (addr >= 0xdff000 && addr < 0xe00000) { + AMIGA.custom.store8(addr, value); + } + else if (addr >= 0xbfd000 && addr < 0xbfdf01) { + AMIGA.cia.store8(addr, value); + } + else if (addr >= 0xbfe001 && addr < 0xbfef02) { + AMIGA.cia.store8(addr, value); + } + else if (addr >= 0xdc0000 && addr < 0xdd0000) { + AMIGA.rtc.store8(addr, value); + } + else if (addr >= this.res_e0.lower && addr < this.res_e0.upper) { + if (addr & 1) { + addr = (addr - this.res_e0.lower) >>> 1; + this.res_e0.data[addr] = (this.res_e0.data[addr] & 0xff00) | value; + } else { + addr = (addr - this.res_e0.lower) >>> 1; + this.res_e0.data[addr] = (value << 8) | (this.res_e0.data[addr] & 0x00ff); + } + } + else if (addr >= this.res_f0.lower && addr < this.res_f0.upper) { + if (addr & 1) { + addr = (addr - this.res_f0.lower) >>> 1; + this.res_f0.data[addr] = (this.res_f0.data[addr] & 0xff00) | value; + } else { + addr = (addr - this.res_f0.lower) >>> 1; + this.res_f0.data[addr] = (value << 8) | (this.res_f0.data[addr] & 0x00ff); + } + } + else if (addr >= this.res_d8.lower && addr < this.res_d8.upper) { + if (addr & 1) { + addr = (addr - this.res_d0.lower) >>> 1; + this.res_d0.data[addr] = (this.res_d0.data[addr] & 0xff00) | value; + } else { + addr = (addr - this.res_d0.lower) >>> 1; + this.res_d0.data[addr] = (value << 8) | (this.res_d0.data[addr] & 0x00ff); + } + } + else if (addr >= this.ac_z2.lower && addr < this.ac_z2.upper) { + AMIGA.expansion.store8(addr, value); + } + //else BUG.info('Memory.store8() ILLEGAL MEMORY ACCESS addr $%08x, val %02x', addr, value); + } + + this.store16 = function (addr, value) { + //BUG.info('Memory.store16() addr $%08x, val $%04x', addr, value); + + if (addr >= 0 && addr < this.chip.size - 1) { + this.chip.data[addr >>> 1] = value; + } + else if (this.slow.enabled && addr >= this.slow.lower && addr < this.slow.upper - 1) { + this.slow.data[(addr - this.slow.lower) >>> 1] = value; + } + else if (this.fast.enabled && addr >= this.fast.lower && addr < this.fast.upper - 1) { + this.fast.data[(addr - this.fast.lower) >>> 1] = value; + } + else if (addr >= 0xdff000 && addr < 0xe00000 - 1) { + AMIGA.custom.store16(addr, value); + } + else if (addr >= 0xbfd000 && addr < 0xbfdf01 - 1) { + AMIGA.cia.store16(addr, value); + } + else if (addr >= 0xbfe001 && addr < 0xbfef02 - 1) { + AMIGA.cia.store16(addr, value); + } + else if (addr >= 0xdc0000 && addr < 0xdd0000 - 1) { + AMIGA.rtc.store16(addr, value); + } + else if (addr >= this.res_e0.lower && addr < this.res_e0.upper - 1) { + this.res_e0.data[(addr - this.res_e0.lower) >>> 1] = value; + } + else if (addr >= this.res_f0.lower && addr < this.res_f0.upper - 1) { + this.res_f0.data[(addr - this.res_f0.lower) >>> 1] = value; + } + else if (addr >= this.res_d8.lower && addr < this.res_d8.upper - 1) { + this.res_d0.data[(addr - this.res_d0.lower) >>> 1] = value; + } + //else BUG.info('Memory.store16() ILLEGAL MEMORY ACCESS addr $%08x, val %04x', addr, value); + } + + this.store32 = function (addr, value) { + //BUG.info('Memory.store32() addr $%08x, val $%08x', addr, value); + + if (addr >= 0 && addr < this.chip.size - 3) { + addr >>>= 1; + this.chip.data[addr] = value >>> 16; + this.chip.data[addr + 1] = value & 0xffff; + } + else if (this.slow.enabled && addr >= this.slow.lower && addr < this.slow.upper - 3) { + addr = (addr - this.slow.lower) >>> 1; + this.slow.data[addr] = value >>> 16; + this.slow.data[addr + 1] = value & 0xffff; + } + else if (this.fast.enabled && addr >= this.fast.lower && addr < this.fast.upper - 3) { + addr = (addr - this.fast.lower) >>> 1; + this.fast.data[addr] = value >>> 16; + this.fast.data[addr + 1] = value & 0xffff; + } + else if (addr >= 0xdff000 && addr < 0xe00000 - 3) { + AMIGA.custom.store32(addr, value); + } + else if (addr >= 0xbfd000 && addr < 0xbfdf01 - 3) { + AMIGA.cia.store32(addr, value); + } + else if (addr >= 0xbfe001 && addr < 0xbfef02 - 3) { + AMIGA.cia.store32(addr, value); + } + else if (addr >= 0xdc0000 && addr < 0xdd0000 - 3) { + AMIGA.rtc.store32(addr, value); + } + else if (addr >= this.res_e0.lower && addr < this.res_e0.upper - 3) { + addr = (addr - this.res_e0.lower) >>> 1; + this.res_e0.data[addr] = value >>> 16; + this.res_e0.data[addr + 1] = value & 0xffff; + } + else if (addr >= this.res_f0.lower && addr < this.res_f0.upper - 3) { + addr = (addr - this.res_f0.lower) >>> 1; + this.res_f0.data[addr] = value >>> 16; + this.res_f0.data[addr + 1] = value & 0xffff; + } + else if (addr >= this.res_d8.lower && addr < this.res_d8.upper - 3) { + addr = (addr - this.res_d0.lower) >>> 1; + this.res_d0.data[addr] = value >>> 16; + this.res_d0.data[addr + 1] = value & 0xffff; + } + //else if (!(addr & 0xc80000)) BUG.info('Memory.store32() ILLEGAL MEMORY ACCESS addr $%08x, val %08x', addr, value); + } + + this.check16_chip = function (addr, size) { + return (addr >= 0 && addr + size < this.chip.size - 1); + } + /*this.load16_chip = function (addr) { + if (this.check16_chip(addr, 1)) { + var v = this.chip.data[addr >>> 1]; + AMIGA.custom.last_value = v; + return v; + } else BUG.info('load16_chip() ILLEGAL MEMORY ACCESS addr %x', addr); + return 0xffff; + } + this.store16_chip = function (addr, value) { + if (this.check16_chip(addr, 1)) { + this.chip.data[addr >>> 1] = value; + AMIGA.custom.last_value = value; + } else BUG.info('store16_chip() ILLEGAL MEMORY ACCESS addr %x, value %x', addr, value); + }*/ + this.load16_chip = function (addr) { + if (addr < this.chip.size - 1) + AMIGA.custom.last_value = this.chip.data[addr >>> 1]; + else + AMIGA.custom.last_value = 0xffff; + + return AMIGA.custom.last_value; + } + this.store16_chip = function (addr, value) { + if (addr < this.chip.size - 1) + this.chip.data[addr >>> 1] = AMIGA.custom.last_value = value; + else + AMIGA.custom.last_value = 0xffff; + } + + this.copy_rom = function(data) { + //BUG.info('copyrom() size %d', data.length); + //BUG.info('copyrom() crc32 $%08x', crc32(data)); + + if (data.length == 0x80000) { + /*var lo = crc32(data.substr(0, 0x40000)); + var hi = crc32(data.substr(0x40000, 0x80000)); + if (lo != hi) { + BUG.info('copyrom() lo crc32 $%08x', lo); + BUG.info('copyrom() hi crc32 $%08x', hi); + }*/ + for (var i = 0; i < data.length; i++) { + var v = data.charCodeAt(i) & 0xff; + if (i & 1) { + var j = i >>> 1; + this.rom.data[j] = (this.rom.data[j] & 0xff00) | v; + } else { + var j = i >>> 1; + this.rom.data[j] = (v << 8) | (this.rom.data[j] & 0x00ff); + } + } + this.rom.lower = 0xf80000; + } + else if (data.length == 0x40000) { + for (var i = 0; i < data.length; i++) { + var v = data.charCodeAt(i) & 0xff; + if (i & 1) { + var j = i >>> 1; + this.rom.data[j] = (this.rom.data[j] & 0xff00) | v; + this.rom.data[0x20000 + j] = (this.rom.data[0x20000 + j] & 0xff00) | v; + } else { + var j = i >>> 1; + this.rom.data[j] = (v << 8) | (this.rom.data[j] & 0x00ff); + this.rom.data[0x20000 + j] = (v << 8) | (this.rom.data[0x20000 + j] & 0x00ff); + } + } + this.rom.lower = 0xfc0000; + } + } + + this.copy_e0 = function(data) { + if (data.length <= 0x80000) { + for (var i = 0; i < data.length; i++) { + var v = data.charCodeAt(i) & 0xff; + if (i & 1) { + var j = i >>> 1; + this.res_e0.data[j] = (this.res_e0.data[j] & 0xff00) | v; + } else { + var j = i >>> 1; + this.res_e0.data[j] = (v << 8) | (this.res_e0.data[j] & 0x00ff); + } + } + } + } + this.copy_f0 = function(data) { + if (data.length <= 0x80000) { + for (var i = 0; i < data.length; i++) { + var v = data.charCodeAt(i) & 0xff; + if (i & 1) { + var j = i >>> 1; + this.res_f0.data[j] = (this.res_f0.data[j] & 0xff00) | v; + } else { + var j = i >>> 1; + this.res_f0.data[j] = (v << 8) | (this.res_f0.data[j] & 0x00ff); + } + } + } + } + + /*this.mirror_rom_to_chipram = function() { + for (var i = 0; i < this.rom.size; i++) + this.chip.data[i] = this.rom.data[i]; + + if (this.ext.size > 0) { + for (var i = 0; i < this.ext.size; i++) + this.chip.data[this.rom.size + i] = this.ext.data[i]; + } + }*/ +} \ No newline at end of file diff --git a/sae/playfield.js b/sae/playfield.js new file mode 100644 index 0000000..223c010 --- /dev/null +++ b/sae/playfield.js @@ -0,0 +1,1376 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Sprite(num) { + this.num = num; + this.pt = 0; + this.pos = 0; + this.ctl = 0; + this.data = 0; + this.datb = 0; + + this.xpos = 0; + this.vstart = 0; + this.vstop = 0; + this.armed = false; + this.dmastate = 0; + this.dmacycle = 0; +} + +function Sprites() { + const MAX_SPRITES = 8; + + this.sprite = new Array(MAX_SPRITES); + for (var i = 0; i < MAX_SPRITES; i++) + this.sprite[i] = new Sprite(i); + + var spr_data = []; + var spr_state = []; + var spr_attach = []; + + var dblpf_ms = null; + var dblpf_ms1 = null; + var dblpf_ms2 = null; + var sprite_offs = null; + + var sprtabb = null; + var sprtaba = null; + var sprite_ab_merge = null; + var sprclx = null; + var clxmask = null; + + this.nr_armed = 0; + var sprite_vblank_endline = 0; + var sprite_mask = 0; + //var sprite_minx, sprite_maxx; + + this.clxdat = 0; + this.clxcon = 0; + var clxcon_bpl_enable = 0; + var clxcon_bpl_match = 0; + + this.init_sprite_tables = function() { + //spr_data = new Array(800); + //spr_state = new Array(800); + //spr_attach = new Array(800); + + dblpf_ms = new Array(256); + dblpf_ms1 = new Array(256); + dblpf_ms2 = new Array(256); + sprite_offs = new Array(256); + + sprtaba = new Array(256); + sprtabb = new Array(256); + sprite_ab_merge = new Array(256); + + for (var i = 0; i < 256; i++) { + var plane1 = ((i >> 0) & 1) | ((i >> 1) & 2) | ((i >> 2) & 4) | ((i >> 3) & 8); + var plane2 = ((i >> 1) & 1) | ((i >> 2) & 2) | ((i >> 3) & 4) | ((i >> 4) & 8); + + dblpf_ms[i] = i == 0 ? 16 : 8; + dblpf_ms1[i] = plane1 == 0 ? (plane2 == 0 ? 16 : 8) : 0; + dblpf_ms2[i] = plane2 == 0 ? (plane1 == 0 ? 16 : 0) : 8; + + sprite_offs[i] = (i & 15) ? 0 : 2; + + sprtaba[i] = ((((i >> 7) & 1) << 0) | (((i >> 6) & 1) << 2) | (((i >> 5) & 1) << 4) | (((i >> 4) & 1) << 6) | (((i >> 3) & 1) << 8) | (((i >> 2) & 1) << 10) | (((i >> 1) & 1) << 12) | (((i >> 0) & 1) << 14)); + sprtabb[i] = sprtaba[i] << 1; + sprite_ab_merge[i] = (((i & 15) ? 1 : 0) | ((i & 240) ? 2 : 0)); + } + + sprclx = new Array(16); + clxmask = new Array(16); + + for (var i = 0; i < 16; i++) { + clxmask[i] = (((i & 1) ? 0xF : 0x3) | ((i & 2) ? 0xF0 : 0x30) | ((i & 4) ? 0xF00 : 0x300) | ((i & 8) ? 0xF000 : 0x3000)); + sprclx[i] = (((i & 0x3) == 0x3 ? 1 : 0) | ((i & 0x5) == 0x5 ? 2 : 0) | ((i & 0x9) == 0x9 ? 4 : 0) | ((i & 0x6) == 0x6 ? 8 : 0) | ((i & 0xA) == 0xA ? 16 : 0) | ((i & 0xC) == 0xC ? 32 : 0)) << 9; + } + } + + this.setup = function() { + sprite_vblank_endline = AMIGA.config.video.ntsc ? 20 : 25; + + if (dblpf_ms == null) this.init_sprite_tables(); + } + + this.cleanup = function() {} + this.reset = function() {} + + this.clear = function() { + spr_data = []; + spr_state = []; + spr_attach = []; + } + + this.setMask = function (pf1p, pf2p) { + sprite_mask = 0xFFFF0000 << (4 * pf2p); + sprite_mask &= 0xFFFFFFFF; + sprite_mask |= (0x0000FFFF << (4 * pf1p)) & 0xFFFF; + sprite_mask &= 0xFFFFFFFF; + } + + this.render = function (pos, apixel) { + if (!spr_data[pos]) return 0; + + var maskshift = AMIGA.playfield.state.dpf ? (AMIGA.playfield.state.pf2pri ? dblpf_ms2[apixel] : dblpf_ms1[apixel]) : dblpf_ms[apixel]; + var plfmask = (sprite_mask >>> maskshift) >>> maskshift; + var v = spr_data[pos] & ~plfmask; + + if (v != 0) { + var v1 = v & 255; + var offs; + var col; + + if (v1 == 0) offs = sprite_offs[v >> 8] + 4; + else offs = sprite_offs[v1]; + + v = (v >> (offs << 1)) & 15; + //v ^= 8; /* debug */ + + if (spr_attach[pos] && (spr_state[pos] & (3 << offs))) { + col = 16 + v; + } else { + var vlo = v & 3; + var vhi = (v & (vlo - 1)) >> 2; + col = 16 + (vlo | vhi) + offs * 2; + } + return col; + } + return 0; + } + + this.spr_plf_collision = function() { + if (clxcon_bpl_enable == 0) { + this.clxdat |= 0x1fe; + return; + } + var collision_mask = clxmask[this.clxcon >> 12]; + + for (var i = 0; i < MAX_SPRITES; i++) { + var sprxp = (this.sprite[i].xpos - AMIGA.playfield.state.hstart + 1) * (AMIGA.playfield.state.hires ? 2 : 1); + + if (!this.sprite[i].armed || sprxp < 0) continue; + + var minpos = sprxp; + var maxpos = sprxp + 16; + + for (var j = minpos; j < maxpos; j++) { + var sprpix = spr_data[j] & collision_mask; + var match = 1; + + if (sprpix == 0) continue; + + sprpix = sprite_ab_merge[sprpix & 255] | (sprite_ab_merge[sprpix >> 8] << 2); + sprpix <<= 1; + + for (var k = 1; k >= 0; k--) { + if (AMIGA.playfield.bplcon0 & 0x400) match = 1; + + for (var l = k; match && l < 6; l += 2) { + if (clxcon_bpl_enable & (1 << l)) { + var t = 0; + if (l < AMIGA.playfield.state.planes) { + t = (AMIGA.playfield.linedata[j] >> l) & 1; + } + if (t != ((clxcon_bpl_match >> l) & 1)) match = 0; + } + } + if (match) { + this.clxdat |= sprpix << (k * 4); + } + } + } + } + } + + this.draw_1 = function (sprxp, bufpos, datab, num, dbl, mask, do_collisions, collision_mask) { + var j = 0; + while (datab) { + var col = 0; + var coltmp = 0; + + //if ((sprxp >= this.sprite_minx && sprxp < this.sprite_maxx) || (AMIGA.playfield.bplcon3 & 2)) + col = (datab & 3) << (2 * num); + /*if (0) { + if (sprxp == this.sprite_minx || sprxp == this.sprite_maxx - 1) + col ^= (rand () << 16) | rand (); + }*/ + //if ((j & mask) == 0) + { + var tmp = spr_data[bufpos] | col; + spr_data[bufpos++] = tmp; + if (do_collisions) coltmp |= tmp; + //sprxp++; + } + if (dbl > 0) { + var tmp = spr_data[bufpos] | col; + spr_data[bufpos++] = tmp; + if (do_collisions) coltmp |= tmp; + //sprxp++; + } + j++; + datab >>>= 2; + if (do_collisions) { + coltmp &= collision_mask; + if (coltmp) { + var shrunk_tmp = sprite_ab_merge[coltmp & 255] | (sprite_ab_merge[coltmp >> 8] << 2); + this.clxdat |= sprclx[shrunk_tmp]; + } + } + } + } + + this.draw = function() { + //if (this.nr_armed == 0) return; + + var nrs = [], posns = []; + var count = 0; + var xoffs = (180 - (AMIGA.playfield.state.hlen >> 1)) << (AMIGA.playfield.state.hires ? 1 : 0); + + for (var i = 0; i < MAX_SPRITES; i++) { + var sprxp = xoffs + ((this.sprite[i].xpos - AMIGA.playfield.state.hstart) << (AMIGA.playfield.state.hires ? 1 : 0)); + + if (!this.sprite[i].armed || sprxp < 0) continue; + + var j, bestp; + + for (bestp = 0; bestp < count; bestp++) { + if (posns[bestp] > sprxp) break; + if (posns[bestp] == sprxp && nrs[bestp] < i) break; + } + for (j = count; j > bestp; j--) { + posns[j] = posns[j - 1]; + nrs[j] = nrs[j - 1]; + } + posns[j] = sprxp; + nrs[j] = i; + count++; + } + //if (count) { for (i = 0; i < count; i++) BUG.info('SPR n %d, x %d', nrs[i], posns[i]); BUG.info('------------'); } + + var collision_mask = clxmask[this.clxcon >> 12]; + + for (i = 0; i < count; i++) { + var sprxp = posns[i]; + var nr = nrs[i]; + var attachment = this.sprite[nr | 1].ctl & 0x80; + var da = this.sprite[nr].data; + var db = this.sprite[nr].datb; + var datab = ((sprtaba[da & 0xFF] << 16) | sprtaba[da >> 8] | (sprtabb[db & 0xFF] << 16) | sprtabb[db >> 8]); + var dbl = AMIGA.playfield.state.hires ? 1 : 0; + var mask = 0; + + if (AMIGA.config.chipset.colLevel != SAEV_Config_Chipset_ColLevel_None && collision_mask) this.draw_1(sprxp, sprxp, datab, nr, dbl, mask, 1, collision_mask); + else this.draw_1(sprxp, sprxp, datab, nr, dbl, mask, 0, collision_mask); + + if (attachment) { + var state = (1 << (nr & ~1)) & 0xff; + for (j = 0; j < 16; j++) { + spr_state[sprxp + j] |= state; + spr_attach[sprxp + j] = true; + } + } + } + } + + this.sprite_fetch = function(n, dma, hpos, cycle, mode) { + var data = AMIGA.custom.last_value; + if (dma) + data = AMIGA.mem.load16_chip(this.sprite[n].pt); + this.sprite[n].pt += 2; + return data; + } + + this.cycle_1 = function (vp, n, cycle, hpos) { + var isdma = AMIGA.dmaen(DMAF_SPREN) || ((n & 1) && this.sprite[n & ~1].dmacycle); + + if (isdma && vp == sprite_vblank_endline) this.sprArm(n, 0); + + if (vp == this.sprite[n].vstart) { + this.sprite[n].dmastate = 1; + //if (n == 0 && cycle == 0) cursorsprite(); + } + if (vp == this.sprite[n].vstop || vp == sprite_vblank_endline) { + this.sprite[n].dmastate = 0; + if (1) { + // roots 2.0 flower zoomer bottom part missing if this enabled + if (vp == this.sprite[n].vstop) { + this.sprArm(n, 0); + //return; + } + } + } + + if (!isdma) return; + if (cycle && !this.sprite[n].dmacycle) return; /* Superfrog intro flashing bee fix */ + + var dma = hpos < AMIGA.playfield.state.dstart; + var posctl = 0; + + if (vp == this.sprite[n].vstop || vp == sprite_vblank_endline) { + this.sprite[n].dmastate = 0; + posctl = 1; + if (dma) { + var data = this.sprite_fetch(n, dma, hpos, cycle, 0); + + if (cycle == 0) { + this.SPRxPOS(data, n); + this.sprite[n].dmacycle = 1; + } else { + this.SPRxCTL(data, n); + this.sprite[n].dmastate = 0; + this.sprStartStop(n); + } + } + } + if (this.sprite[n].dmastate && !posctl && dma) { + var data = this.sprite_fetch(n, dma, hpos, cycle, 1); + + if (cycle == 0) { + this.SPRxDATA(data, n); + this.sprite[n].dmacycle = 1; + } else { + this.SPRxDATB(data, n); + this.sprArm(n, 1); + } + } + } + + this.cycle = function (hpos) { + var vp = AMIGA.events.vpos; + if (vp < sprite_vblank_endline) return; + + for (var i = 0; i < MAX_SPRITES * 2; i++) { + this.sprite[i >> 1].dmacycle = 1; + this.cycle_1(vp, i >> 1, i & 1, 0); + } + } + + this.sprArm = function (n, state) { + switch (state) { + case 0: + this.nr_armed -= this.sprite[n].armed; + this.sprite[n].armed = 0; + break; + default: + this.nr_armed += 1 - this.sprite[n].armed; + this.sprite[n].armed = 1; + } + } + + this.sprStartStop = function (n) { + if (AMIGA.events.vpos + 1 == this.sprite[n].vstart) this.sprite[n].dmastate = 1; + if (AMIGA.events.vpos + 1 == this.sprite[n].vstop) this.sprite[n].dmastate = 0; + } + + this.SPRxCTLPOS = function (n) { + this.sprStartStop(n); + + this.sprite[n].xpos = (this.sprite[n].pos & 0xff) * 2 + (this.sprite[n].ctl & 1); + this.sprite[n].vstart = (this.sprite[n].pos >> 8) | ((this.sprite[n].ctl << 6) & 0x100); + this.sprite[n].vstop = (this.sprite[n].ctl >> 8) | ((this.sprite[n].ctl << 7) & 0x100); + /*if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS) { + this.sprite[n].vstart |= (this.sprite[n].ctl << 3) & 0x200; + this.sprite[n].vstop |= (this.sprite[n].ctl << 4) & 0x200; + }*/ + this.sprStartStop(n); + } + + this.SPRxCTL = function (v, n) { + this.sprite[n].ctl = v; + this.sprArm(n, 0); + this.SPRxCTLPOS(n); + } + + this.SPRxPOS = function (v, n) { + this.sprite[n].pos = v; + this.SPRxCTLPOS(n); + } + + this.SPRxDATA = function (v, n) { + this.sprite[n].data = v; + this.sprArm(n, 1); + } + + this.SPRxDATB = function (v, n) { + this.sprite[n].datb = v; + } + + this.SPRxPTH = function (v, n) { + this.sprite[n].pt = ((v << 16) | (this.sprite[n].pt & 0xffff)) >>> 0; + } + + this.SPRxPTL = function (v, n) { + this.sprite[n].pt = ((this.sprite[n].pt & 0xffff0000) | v) >>> 0; + } + + this.CLXCON = function (v) { + this.clxcon = v; + clxcon_bpl_enable = (v >> 6) & 63; + clxcon_bpl_match = v & 63; + } + + this.CLXDAT = function() { + var v = this.clxdat | 0x8000; + this.clxdat = 0; + return v; + } +} + +function Plane(num) { + this.num = num; + this.pt = 0; + this.dat = -1; + this.line = new Uint16Array(45); +} + +function Playfield() { + /* BPLCON0 flags */ + const BPLF_ERSY = 1 << 1; + const BPLF_LACE = 1 << 2; + const BPLF_LPEN = 1 << 3; + const BPLF_GAUD = 1 << 8; + const BPLF_COLOR = 1 << 9; + const BPLF_DBLPF = 1 << 10; + const BPLF_HOMOD = 1 << 11; + const BPLF_HIRES = 1 << 15; + /* BPLCON2 flags */ + const BPLF_PF2PRI = 1 << 6; + + this.sprites = new Sprites(); + + this.plane = new Array(6); + for (var i = 0; i < 6; i++) + this.plane[i] = new Plane(i); + + this.color_std = new Uint16Array(32); + this.color_ehb = new Uint16Array(32); + for (var i = 0; i < 32; i++) this.color_std[i] = this.color_ehb[i] = 0; + + this.linedata = []; + + this.bplcon0 = 0; + this.bplcon1 = 0; + this.bplcon2 = 0; + this.bplcon3 = 0; + + this.bpl1mod = 0; + this.bpl2mod = 0; + + this.diwstrt = 0; + this.diwstop = 0; + this.diwhigh = -1; //ECS + this.ddfstrt = 0; + this.ddfstop = 0; + + this.state = { + vstart: 0, + vstop: 0, + vlen: 0, + vlenreal: 0, + vlenrealset: 0, + vlenrealtmp: 0, + hstart: 0, + hstop: 0, + hlen: 0, + hnum: 0, + hpixel: 0, + hleft: 0, + dstart: 0, + dstop: 0, + dlen: 0, + dnum: 0, + dwords: 0, + ddiff: 0, + altget: false, + hires: false, + ilace: false, + std: false, + ehb: false, + dpf: false, + ham: false, + planes: 0, + delay1: 0, + delay2: 0, + pf2pri: false, + pf1p: 0, + pf2p: 0, + colorOnly: true, + hScale: true, + vScale: true, + }; + + this.frame = { + skip: false, + skipcount: 0, + count: 0 + }; + + /* for playfield development */ + /*this.lamps = function() { + var e = document.getElementById('lamp_hir'); + if (e) e.style.color = this.state.hires ? '#8c8' : '#888'; + e = document.getElementById('lamp_ila'); + if (e) e.style.color = this.state.ilace ? '#8c8' : '#888'; + e = document.getElementById('lamp_dpf'); + if (e) e.style.color = this.state.dpf ? '#8c8' : '#888'; + e = document.getElementById('lamp_ham'); + if (e) e.style.color = this.state.ham ? '#8c8' : '#888'; + e = document.getElementById('lamp_ehb'); + if (e) e.style.color = this.state.ehb ? '#8c8' : '#888'; + e = document.getElementById('lamp_scr'); + if (e) e.style.color = this.state.altget ? '#8c8' : '#888'; + e = document.getElementById('lamp_p2p'); + if (e) e.style.color = this.state.pf2pri ? '#8c8' : '#888'; + e = document.getElementById('lamp_nPl'); + if (e) e.innerHTML = this.state.planes; + e = document.getElementById('lamp_dl1'); + if (e) e.innerHTML = sprintf('%02d', this.state.delay1); + e = document.getElementById('lamp_dl2'); + if (e) e.innerHTML = sprintf('%02d', this.state.delay2); + //if (this.state.hlen > 0 && this.state.hlen <= 720 && this.state.vlen > 0 && this.state.vlen <= 288) + { + e = document.getElementById('lamp_diw'); + if (e) e.innerHTML = sprintf('%dx%d', this.state.hlen, this.state.vlenreal); + } + e = document.getElementById('lamp_dff'); + if (e) e.innerHTML = sprintf('%d (%d)', this.state.dlen, this.state.ddiff); + }*/ + + + this.setup = function() { + this.create_cycle_diagram_table(); + this.sprites.setup(); + } + + this.cleanup = function() { + this.sprites.cleanup(); + } + + this.reset = function() { + this.state.colorOnly = true; + this.state.hScale = true; + this.state.vScale = true; + + for (var i = 0; i < 32; i++) + this.color_std[i] = this.color_ehb[i] = 0; + + this.frame.skip = false; + this.frame.skipcount = 0; + } + + this.recalc = function() { + this.state.vlen = this.state.vstop - this.state.vstart; + this.state.hlen = this.state.hstop - this.state.hstart; + this.state.hnum = this.state.hlen >> 3; + + this.state.dlen = (this.state.dstop - this.state.dstart + 15) & ~7; + this.state.dnum = this.state.dlen >> 3; + this.state.ddiff = (this.state.dnum << 1) - this.state.hnum; + if (this.state.ddiff < 0) this.state.ddiff = 0; + + //this.state.hlen = this.state.dlen << 1; + + this.state.hpixel = this.state.hlen << (this.state.hires ? 1 : 0); + this.state.hleft = (180 - Math.min(this.state.hlen >> 1, this.state.dlen)) << (this.state.hires ? 1 : 0); + this.state.dwords = this.state.dnum << (this.state.hires ? 1 : 0); + + this.state.altget = this.state.ddiff != 0 && ((this.state.dstart >> 3) & 1) != 0; + + //this.lamps(); + } + + this.calcdiw = function() { + this.state.vstart = this.diwstrt >> 8; + this.state.vstop = this.diwstop >> 8; + this.state.hstart = this.diwstrt & 0xff; + this.state.hstop = this.diwstop & 0xff; + this.state.hstart &= ~7; + this.state.hstop &= ~7; + + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS && this.diwhigh != -1) { + this.state.vstart |= (this.diwhigh & 7) << 8; + this.state.vstop |= ((this.diwhigh >> 8) & 7) << 8; + this.state.hstart |= ((this.diwhigh >> 5) & 1) << 8; + this.state.hstop |= ((this.diwhigh >> 13) & 1) << 8; + } else { + if ((this.state.vstop & 0x80) == 0) this.state.vstop |= 0x100; + this.state.hstop += 0x100; + } + + this.recalc(); + //BUG.info('Playfield.calcdiw() diwstrt $%04x diwstop $%04x | v %d-%d, h %d-%d | %d x %d', this.diwstrt, this.diwstop, this.state.vstart, this.state.vstop, this.state.hstart, this.state.hstop, this.state.hlen, this.state.vlen); + } + + this.calcddf = function(stop) { + this.state.dstart = this.ddfstrt & 0xff; + this.state.dstop = this.ddfstop & 0xff; + this.state.dstart &= ~3; + this.state.dstop &= ~3; + + if (this.state.dstart < 0x18) this.state.dstart = 0x18; + if (this.state.dstop > 0xd8) this.state.dstop = 0xd8; + + /*if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS) { + if (this.state.dstop > AMIGA.events.maxhpos) this.state.dstart = 0; + if (this.state.dstart < 0x18) this.state.dstart = 0x18; + } else { + if (this.state.dstart >= this.state.dstop && this.state.dstart >= 0x18) this.state.dstop = 0xff; + }*/ + + this.recalc(); + //BUG.info('Playfield.calcddf() ddfstrt $%04x ddfstop $%04x | plflinelen %d', this.ddfstrt, this.ddfstop, this.state.dlen); + } + + + this.drawpixel = function (xpos, ypos, rgb) { + /*if (this.state.ilace) { + if (this.state.hires) + AMIGA.video.drawpixel(xpos, ypos, rgb); + else { + AMIGA.video.drawpixel(xpos*2, ypos, rgb); + AMIGA.video.drawpixel(xpos*2+1, ypos, rgb); + } + } else { + if (this.state.hires) { + AMIGA.video.drawpixel(xpos, ypos*2, rgb); + AMIGA.video.drawpixel(xpos, ypos*2+1, rgb); + } else { + AMIGA.video.drawpixel(xpos*2, ypos*2, rgb); + AMIGA.video.drawpixel(xpos*2+1, ypos*2, rgb); + AMIGA.video.drawpixel(xpos*2, ypos*2+1, rgb); + AMIGA.video.drawpixel(xpos*2+1, ypos*2+1, rgb); + } + }*/ + + if (this.state.hires) + AMIGA.video.drawpixel(xpos, ypos, rgb); + else + AMIGA.video.draw2pixel(xpos << 1, ypos, rgb); + } + this.drawpixel_std = function (xpos, ypos, col) { + var spritecol = this.sprites.render(xpos, col); + var rgb = this.color_std[spritecol ? spritecol : col]; + this.linedata[xpos] = col; + //AMIGA.video.drawpixel(xpos, ypos, rgb); + this.drawpixel(xpos, ypos, rgb); + } + + this.drawpixel_ehb = function (xpos, ypos, col, ehb) { + var rgb; + var spritecol = this.sprites.render(xpos, col); + if (spritecol) rgb = this.color_std[spritecol]; + else rgb = ehb ? this.color_ehb[col] : this.color_std[col]; + this.linedata[xpos] = col; + //AMIGA.video.drawpixel(xpos, ypos, rgb); + this.drawpixel(xpos, ypos, rgb); + } + + this.drawpixel_dpf = function (xpos, ypos, col1, col2) { + var rgb, col = this.state.pf2pri ? col2 : col1; + var spritecol = this.sprites.render(xpos, col); + if (spritecol) + rgb = this.color_std[spritecol]; + else { + col = this.state.pf2pri ? (col2 > 0 ? (8 + col2) : col1) : (col1 > 0 ? col1 : (8 + col2)); + rgb = this.color_std[col]; + } + this.linedata[xpos] = col; + //AMIGA.video.drawpixel(xpos, ypos, rgb); + this.drawpixel(xpos, ypos, rgb); + } + + this.drawpixel_ham_col = function (xpos, ypos, col) { + var spritecol = this.sprites.render(xpos, col); + var rgb = this.color_std[spritecol ? spritecol : col] + this.linedata[xpos] = col; + //AMIGA.video.drawpixel(xpos, ypos, rgb); + this.drawpixel(xpos, ypos, rgb); + } + + this.drawpixel_ham_rgb = function (xpos, ypos, rgb) { + var spritecol = this.sprites.render(xpos, 0); + if (spritecol) rgb = this.color_std[spritecol]; + this.linedata[xpos] = 0; + //AMIGA.video.drawpixel(xpos, ypos, rgb); + this.drawpixel(xpos, ypos, rgb); + } + + + this.drawline_std = function(x, y, o) { + var i, j, l; + if (this.state.altget && this.state.ddiff < 4) { + for (j = 0; j < 4; j++) AMIGA.video.drawpixel(x + j, y, this.color_std[0]); + + for (j = 4, l = 11; j < 16; j++, l--) { + var col = 0; + switch (this.state.planes) { + case 5: col = ((this.plane[4].line[o] >> l) & 1); + case 4: col = (col << 1) | ((this.plane[3].line[o] >> l) & 1); + case 3: col = (col << 1) | ((this.plane[2].line[o] >> l) & 1); + case 2: col = (col << 1) | ((this.plane[1].line[o] >> l) & 1); + case 1: col = (col << 1) | ((this.plane[0].line[o] >> l) & 1); + } + this.drawpixel_std(x + j, y, col); + } + i = 16; o++; + } else + i = 0; + + for (; i < this.state.hpixel; i += 16, o++) { + for (j = 0, l = 15; j < 16; j++, l--) { + var col = 0; + switch (this.state.planes) { + case 5: col = ((this.plane[4].line[o] >> l) & 1); + case 4: col = (col << 1) | ((this.plane[3].line[o] >> l) & 1); + case 3: col = (col << 1) | ((this.plane[2].line[o] >> l) & 1); + case 2: col = (col << 1) | ((this.plane[1].line[o] >> l) & 1); + case 1: col = (col << 1) | ((this.plane[0].line[o] >> l) & 1); + } + this.drawpixel_std(x + i + j, y, col); + } + } + } + + this.drawline_ehb = function(x, y, o) { + for (var i = 0; i < this.state.hpixel; i += 16, o++) { + for (var j = 0, l = 15; j < 16; j++, l--) { + var ehb = ((this.plane[5].line[o] >> l) & 1); + var col = ((this.plane[4].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[3].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[2].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[1].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[0].line[o] >> l) & 1); + this.drawpixel_ehb(x + i + j, y, col, ehb); + } + } + } + + this.drawline_dpf = function(x, y, o) { + for (var i = 0; i < this.state.hpixel; i += 16, o++) { + for (var j = 0, l = 15; j < 16; j++, l--) { + var col1 = 0, col2 = 0; + switch (this.state.planes) { + case 6: col2 = ((this.plane[5].line[o] >> l) & 1); + case 5: col1 = ((this.plane[4].line[o] >> l) & 1); + case 4: col2 = (col2 << 1) | ((this.plane[3].line[o] >> l) & 1); + case 3: col1 = (col1 << 1) | ((this.plane[2].line[o] >> l) & 1); + case 2: col2 = (col2 << 1) | ((this.plane[1].line[o] >> l) & 1); + case 1: col1 = (col1 << 1) | ((this.plane[0].line[o] >> l) & 1); + } + this.drawpixel_dpf(x + i + j, y, col1, col2); + } + } + } + + this.drawline_ham = function(x, y, o) { + var opm, col, r = 0, g = 0, b = 0; + + for (var i = 0; i < this.state.hpixel; i += 16, o++) { + for (var j = 0, l = 15; j < 16; j++, l--) { + opm = (((this.plane[5].line[o] >> l) & 1) << 1) | ((this.plane[4].line[o] >> l) & 1); + col = ((this.plane[3].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[2].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[1].line[o] >> l) & 1); + col = (col << 1) | ((this.plane[0].line[o] >> l) & 1); + switch (opm) { + case 2: { r = col; this.drawpixel_ham_rgb(x + i + j, y, ((r << 1) << 11) | ((g << 2) << 5) | (b << 1)); break; } + case 3: { g = col; this.drawpixel_ham_rgb(x + i + j, y, ((r << 1) << 11) | ((g << 2) << 5) | (b << 1)); break; } + case 1: { b = col; this.drawpixel_ham_rgb(x + i + j, y, ((r << 1) << 11) | ((g << 2) << 5) | (b << 1)); break; } + default: { + r = ((this.color_std[col] >> 11) & 31) >> 1; + g = ((this.color_std[col] >> 5) & 63) >> 2; + b = (this.color_std[col] & 31) >> 1; + this.drawpixel_ham_col(x + i + j, y, col); + } + } + } + } + } + + this.drawline_border = function(y) { + var l = this.state.hleft; + var w = this.state.hpixel; + var r = this.state.hires ? VIDEO_WIDTH : VIDEO_WIDTH >> 1; + var x, rgb = this.color_std[0]; + + if (!this.state.hires) { + l <<= 1; + w <<= 1; + r <<= 1; + } + AMIGA.video.drawline_from_to(0, l, y, rgb); + AMIGA.video.drawline_from_to(l + w, r, y, rgb); + } + this.drawline_empty = function(y) { + var l = this.state.hleft; + var w = this.state.hpixel; + var x, rgb = this.color_std[0]; + + if (!this.state.hires) { + l <<= 1; + w <<= 1; + } + AMIGA.video.drawline_from_to(l, l + w, y, rgb); + } + + this.fetchline = function() { + var pt5 = this.plane[5].pt >>> 1; + var pt4 = this.plane[4].pt >>> 1; + var pt3 = this.plane[3].pt >>> 1; + var pt2 = this.plane[2].pt >>> 1; + var pt1 = this.plane[1].pt >>> 1; + var pt0 = this.plane[0].pt >>> 1; + + if (this.state.altget) { + switch (this.state.planes) { + case 6: { this.plane[5].line[0] = AMIGA.mem.chip.data[pt5] >> 8; } + case 5: { this.plane[4].line[0] = AMIGA.mem.chip.data[pt4] >> 8; } + case 4: { this.plane[3].line[0] = AMIGA.mem.chip.data[pt3] >> 8; } + case 3: { this.plane[2].line[0] = AMIGA.mem.chip.data[pt2] >> 8; } + case 2: { this.plane[1].line[0] = AMIGA.mem.chip.data[pt1] >> 8; } + case 1: { this.plane[0].line[0] = AMIGA.mem.chip.data[pt0] >> 8; } + } + for (var i = 1; i < this.state.dwords + 1; i++) { + switch (this.state.planes) { + case 6: { this.plane[5].line[i] = ((AMIGA.mem.chip.data[pt5] & 0xff) << 8) | (AMIGA.mem.chip.data[++pt5] >> 8); } + case 5: { this.plane[4].line[i] = ((AMIGA.mem.chip.data[pt4] & 0xff) << 8) | (AMIGA.mem.chip.data[++pt4] >> 8); } + case 4: { this.plane[3].line[i] = ((AMIGA.mem.chip.data[pt3] & 0xff) << 8) | (AMIGA.mem.chip.data[++pt3] >> 8); } + case 3: { this.plane[2].line[i] = ((AMIGA.mem.chip.data[pt2] & 0xff) << 8) | (AMIGA.mem.chip.data[++pt2] >> 8); } + case 2: { this.plane[1].line[i] = ((AMIGA.mem.chip.data[pt1] & 0xff) << 8) | (AMIGA.mem.chip.data[++pt1] >> 8); } + case 1: { this.plane[0].line[i] = ((AMIGA.mem.chip.data[pt0] & 0xff) << 8) | (AMIGA.mem.chip.data[++pt0] >> 8); } + } + } + switch (this.state.planes) { + case 6: { this.plane[5].line[i] = 0; } + case 5: { this.plane[4].line[i] = 0; } + case 4: { this.plane[3].line[i] = 0; } + case 3: { this.plane[2].line[i] = 0; } + case 2: { this.plane[1].line[i] = 0; } + case 1: { this.plane[0].line[i] = 0; } + } + } else { + for (var i = 0; i < this.state.dwords; i++) { + switch (this.state.planes) { + case 6: { this.plane[5].line[i] = AMIGA.mem.chip.data[pt5++]; } + case 5: { this.plane[4].line[i] = AMIGA.mem.chip.data[pt4++]; } + case 4: { this.plane[3].line[i] = AMIGA.mem.chip.data[pt3++]; } + case 3: { this.plane[2].line[i] = AMIGA.mem.chip.data[pt2++]; } + case 2: { this.plane[1].line[i] = AMIGA.mem.chip.data[pt1++]; } + case 1: { this.plane[0].line[i] = AMIGA.mem.chip.data[pt0++]; } + } + } + } + + switch (this.state.planes) { + case 6: this.plane[5].pt = pt5 << 1; + case 5: this.plane[4].pt = pt4 << 1; + case 4: this.plane[3].pt = pt3 << 1; + case 3: this.plane[2].pt = pt2 << 1; + case 2: this.plane[1].pt = pt1 << 1; + case 1: this.plane[0].pt = pt0 << 1; + } + } + + this.scrollline = function(d1, d2) { + var d1n = 16 - d1; + var d2n = 16 - d2; + for (var i = this.state.dwords - 1; i > 0; i--) { + switch (this.state.planes) { + case 6: { if (d2) this.plane[5].line[i] = (this.plane[5].line[i] >> d2) | (this.plane[5].line[i - 1] << d2n); } + case 5: { if (d1) this.plane[4].line[i] = (this.plane[4].line[i] >> d1) | (this.plane[4].line[i - 1] << d1n); } + case 4: { if (d2) this.plane[3].line[i] = (this.plane[3].line[i] >> d2) | (this.plane[3].line[i - 1] << d2n); } + case 3: { if (d1) this.plane[2].line[i] = (this.plane[2].line[i] >> d1) | (this.plane[2].line[i - 1] << d1n); } + case 2: { if (d2) this.plane[1].line[i] = (this.plane[1].line[i] >> d2) | (this.plane[1].line[i - 1] << d2n); } + case 1: { if (d1) this.plane[0].line[i] = (this.plane[0].line[i] >> d1) | (this.plane[0].line[i - 1] << d1n); } + } + } + switch (this.state.planes) { + case 6: { if (d2) this.plane[5].line[i] = this.plane[5].line[i] >> d2; } + case 5: { if (d1) this.plane[4].line[i] = this.plane[4].line[i] >> d1; } + case 4: { if (d2) this.plane[3].line[i] = this.plane[3].line[i] >> d2; } + case 3: { if (d1) this.plane[2].line[i] = this.plane[2].line[i] >> d1; } + case 2: { if (d2) this.plane[1].line[i] = this.plane[1].line[i] >> d2; } + case 1: { if (d1) this.plane[0].line[i] = this.plane[0].line[i] >> d1; } + } + } + + this.mod = function() { + switch (this.state.planes) { + case 6: this.plane[5].pt += this.bpl2mod; + case 5: this.plane[4].pt += this.bpl1mod; + case 4: this.plane[3].pt += this.bpl2mod; + case 3: this.plane[2].pt += this.bpl1mod; + case 2: this.plane[1].pt += this.bpl2mod; + case 1: this.plane[0].pt += this.bpl1mod; + } + } + + this.hSync = function() { + if (!this.frame.skip) { + var x = this.state.hleft; + var y = AMIGA.events.vpos - ((this.state.vlenreal > 256) ? this.state.vstart : 0x2c); + + if (this.state.ilace) y = y * 2 + 1 - AMIGA.events.lof; + if (x < 0) x = 0; + if (y < 0) y = 0; else if (y >= VIDEO_HEIGHT) y = VIDEO_HEIGHT - 1; + + if (AMIGA.events.vpos >= this.state.vstart && AMIGA.events.vpos < this.state.vstop) { + if (this.state.planes > 0 && AMIGA.dmaen(DMAF_BPLEN)) { + var offs = this.state.ddiff ? (this.state.ddiff - 1) >> 1 : 0; + if (this.state.ddiff == 2) { this.state.altget = false; offs++ }; + this.state.colorOnly = false; + + if (this.sprites.nr_armed > 0) this.sprites.draw(); + + this.fetchline(); + this.mod(); + + if (this.state.delay1 || this.state.delay2) { + var d1 = this.state.hires ? ((this.state.delay1 & 7) << 1) : this.state.delay1; + var d2 = this.state.hires ? ((this.state.delay2 & 7) << 1) : this.state.delay2; + this.scrollline(d1, d2); + } + + + if (this.state.std) this.drawline_std(x, y, offs); + else if (this.state.ehb) this.drawline_ehb(x, y, offs); + else if (this.state.dpf) this.drawline_dpf(x, y, offs); + else if (this.state.ham) this.drawline_ham(x, y, offs); + else AMIGA.video.drawline(y, this.color_std[0]); + + this.drawline_border(y); + + if (this.state.vlenrealset == 0) { + this.state.vlenrealset++; + this.state.vlenrealtmp = AMIGA.events.vpos; + } + } else { + AMIGA.video.drawline(y, this.color_std[0]); + } + } else { + if (this.state.vlenrealset == 1) { + this.state.vlenrealset++; + this.state.vlenreal = AMIGA.events.vpos - this.state.vlenrealtmp; + //this.lamps(); + } + AMIGA.video.drawline(y, this.color_std[0]); + } + } + + //AMIGA.events.eventtab[EV_RENDER].oldcycles = AMIGA.events.currcycle; + //AMIGA.events.eventtab[EV_RENDER].evtime = AMIGA.events.currcycle + AMIGA.events.maxhpos * CYCLE_UNIT; + } + + this.vSync = function() { + if (!this.frame.skip && (!this.state.ilace || (this.state.ilace && AMIGA.events.lof == 0))) { + AMIGA.video.draw(this.color_std[0], this.state.hScale, this.state.vScale, this.state.colorOnly); + + //if (!this.state.colorOnly) AMIGA.video.clear_pixels(); + } + + if (this.state.ilace) { if (AMIGA.events.lof == 0) this.frame.count++; } else this.frame.count++; if (this.frame.count == AMIGA.events.hz) this.frame.count = 0; + + /*this.state.hires = false; + this.state.ilace = false; + this.state.ham = false; + this.state.dpf = false; + this.state.ehb = false; + this.state.std = false; + this.state.altget = false; + this.state.hScale = true; + this.state.vScale = true;*/ + + this.state.colorOnly = true; + this.state.vlenrealset = 0; + } + + this.COLORxx = function (v, num) { + //BUG.info('Playfield.COLOR%02d() $%04x', num, v); + var r = (v >> 8) & 15; + var g = (v >> 4) & 15; + var b = v & 15; + this.color_std[num] = ((r << 1) << 11) | ((g << 2) << 5) | (b << 1); + this.color_ehb[num] = (r << 11) | ((g << 1) << 5) | b; + } + + this.BPLxPTx = function (v, num, hi) { + if (hi) { + this.plane[num].pt = ((v << 16) | (this.plane[num].pt & 0xffff)) >>> 0; + } else { + /*if (AMIGA.copper.access && this.is_bitplane_dma(AMIGA.events.hpos() + 1) == num + 1) { + console.log('BPLxPTx() SKIP! ', num, AMIGA.events.hpos()); + return; + }*/ + this.plane[num].pt = ((this.plane[num].pt & 0xffff0000) | v) >>> 0; + } + } + + this.BPLxDAT = function (v, num) { + //if (v) BUG.info('Custom.BPL%dDAT() $%04x', num, v); + this.plane[num].dat = v; + } + + /*this.BPLCON0_getRes = function (v) { + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_OCS) + v &= ~0x40; + return (v & 0x8000) ? 2 : ((v & 0x40) ? 3 : 1); + }*/ + + this.BPLCON0_getPlanes = function (v) { + if ((v & 0x0010) && (v & 0x7000)) + return 0; + if (v & 0x0010) + return 8; + return (v >> 12) & 7; + } + + this.BPLCON0 = function (v) { + //BUG.info('Custom.BPLCON0() $%04x', v); + //var hires_old = this.state.hires; + var ilace_old = this.state.ilace; + + if (!(this.bplcon0 & 2)) { + AMIGA.events.vpos_previous = AMIGA.events.vpos; + AMIGA.events.hpos_previous = AMIGA.events.hpos(); + } + + this.bplcon0 = v; + this.state.planes = this.BPLCON0_getPlanes(v); + + this.state.hires = (v & BPLF_HIRES) != 0; + this.state.ilace = (v & BPLF_LACE) != 0; + this.state.dpf = (v & BPLF_DBLPF) != 0; + this.state.ham = (v & BPLF_HOMOD) != 0; + this.state.ehb = (this.state.planes == 6 && !this.state.dpf && !this.state.ham); + this.state.std = (!this.state.ehb && !this.state.dpf && !this.state.ham); + + this.state.hpixel = this.state.hlen << (this.state.hires ? 1 : 0); + this.state.hleft = (180 - Math.min(this.state.hlen >> 1, this.state.dlen)) << (this.state.hires ? 1 : 0); + this.state.dwords = this.state.dnum << (this.state.hires ? 1 : 0); + + if (this.state.planes) { + this.state.hScale = !this.state.hires; + this.state.vScale = !this.state.ilace; + } + + //if (this.state.hires != hires_old) BUG.info('Custom.BPLCON0() hires change %d, vpos %d, hpos %d', this.state.hires?1:0, AMIGA.events.vpos, AMIGA.events.hpos()); + if (this.state.ilace != ilace_old) { + //BUG.info('Custom.BPLCON0() ilace change %d, vpos %d, hpos %d', this.state.ilace?1:0, AMIGA.events.vpos, AMIGA.events.hpos()); + if (!this.state.ilace) AMIGA.video.clear_pixels(); + AMIGA.audio.recalc_sample_evtime(); + } + + this.setup_fmodes(AMIGA.events.hpos()); + + //this.lamps(); + } + + this.BPLCON1 = function (v) { + this.bplcon1 = v; + this.state.delay1 = v & 0xf; + this.state.delay2 = (v >> 4) & 0xf; + + //this.lamps(); + } + + this.BPLCON2 = function (v) { + this.bplcon2 = v; + this.state.pf2pri = (v & BPLF_PF2PRI) != 0; + this.state.pf1p = v & 7; + this.state.pf2p = (v >> 3) & 7; + + this.sprites.setMask(this.state.pf1p, this.state.pf2p); + + //this.lamps(); + } + + this.BPLCON3 = function (v) { + this.bplcon3 = v; + } + + this.BPL1MOD = function (v) { + //if (v) BUG.info('Custom.BPL1MOD() $%04x', v); + this.bpl1mod = (v & 0x8000) ? (v - 0x10000) : v; + } + + this.BPL2MOD = function (v) { + //if (v) BUG.info('Custom.BPL2MOD() $%04x', v); + this.bpl2mod = (v & 0x8000) ? (v - 0x10000) : v; + } + + this.DIWSTRT = function (v) { + //BUG.info('Custom.DIWSTRT() $%04x', v); + this.diwstrt = v; + this.calcdiw(); + } + + this.DIWSTOP = function (v) { + //BUG.info('Custom.DIWSTOP() $%04x', v); + this.diwstop = v; + this.calcdiw(); + } + + this.DDFSTRT = function (v) { + //BUG.info('Custom.DDFSTRT() $%04x', v); + this.ddfstrt = v; + this.calcddf(0); + } + + this.DDFSTOP = function (v) { + //BUG.info('Custom.DDFSTOP() $%04x', v); + this.ddfstop = v; + this.calcddf(1); + } + + /* ECS + * 7-4 Lisa/Denise/ECS Denise Revision level (decrement to bump revision level, hex F represents 0th rev. level) + * 3 Maintain as a 1 for future generation + * 2 When low indicates AA feature set (LISA) + * 1 When low indicates ECS feature set (LISA or ECS DENISE) + * 0 Maintain as a 1 for future generation + * + * Denise ID: OCS = 0xFF, ECS = 0xFC, AGA = 0xF8 + */ + this.DENISEID___ = function() { + var smb = 0xff; + + var v = ((0xf - AMIGA.config.chipset.agnus_rev) << 4); + v |= 8; + v |= 4; /* low = AGA */ + /* v |= 2; low = ECS */ + //v |= 1; + + BUG.info('Custom.DENISEID() $%04x', (smb << 8) | v); + return (smb << 8) | v; + } + + this.DENISEID = function() { + //if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_AGA) return [0, 0x00F8]; + if (AMIGA.config.chipset.type == SAEV_Config_Chipset_Type_ECS) return [0, 0xFFFC]; + if (AMIGA.config.cpu.model == 68000 && (AMIGA.config.cpu.compatible || AMIGA.config.cpu.exact)) + return [1, 0xFFFF]; + return [0, 0xFFFF]; + } + + this.DIWHIGH = function (v) { + //if (!(AMIGA.config.chipset.type == CHIPSET_AGA)) + v &= ~ (0x0008 | 0x0010 | 0x1000 | 0x0800); + v &= ~ (0x8000 | 0x4000 | 0x0080 | 0x0040); + + //BUG.info('Custom.DIWHIGH() $%04x', v); + this.diwhigh = v; + this.calcdiw(); + } + + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + /*-----------------------------------------------------------------------*/ + + var bplcon0_res = 0; + var bplcon0_planes = 0; + var bplcon0_planes_limit = 0; + + var real_bitplane_number = []; //[3][3][9]; + var fmode = 0; + var fetchmode = 0; + var fetchunits = [ 8,8,8,0, 16,8,8,0, 32,16,8,0 ]; + var fetchstarts = [ 3,2,1,0, 4,3,2,0, 5,4,3,0 ]; + var fm_maxplanes = [ 3,2,1,0, 3,3,2,0, 3,3,3,0 ]; + var fetchunit = 0; + + var fetchunit_mask = 0; + var fetchstart_shift = 0; + var fetchstart = 0; + var fetchstart_mask = 0; + + var cycle_diagram_table = []; //[3][3][9][32]; + var cycle_diagram_free_cycles = []; //[3][3][9]; + var cycle_diagram_total_cycles = []; //[3][3][9]; + var cycle_sequences = [ [2,1,2,1,2,1,2,1], [4,2,3,1,4,2,3,1], [8,4,6,2,7,3,5,1] ]; + var curr_diagram = []; + + this.create_cycle_diagram_table = function () { + var fm, res, cycle, planes, rplanes, v; + var fetch_start, max_planes, freecycles; + var cycle_sequence; + var i, j, k, l; + + for (i = 0; i < 3; i++) { + real_bitplane_number[i] = []; + cycle_diagram_free_cycles[i] = []; + cycle_diagram_total_cycles[i] = []; + for (j = 0; j < 3; j++) { + real_bitplane_number[i][j] = []; + cycle_diagram_free_cycles[i][j] = []; + cycle_diagram_total_cycles[i][j] = []; + for (k = 0; k < 9; k++) { + real_bitplane_number[i][j][k] = 0; + cycle_diagram_free_cycles[i][j][k] = 0; + cycle_diagram_total_cycles[i][j][k] = 0; + } + } + } + for (i = 0; i < 3; i++) { + cycle_diagram_table[i] = []; + for (j = 0; j < 3; j++) { + cycle_diagram_table[i][j] = []; + for (k = 0; k < 9; k++) { + cycle_diagram_table[i][j][k] = []; + for (l = 0; l < 32; l++) + cycle_diagram_table[i][j][k][l] = 0; + } + } + } + + for (fm = 0; fm <= 2; fm++) { + for (res = 0; res <= 2; res++) { + max_planes = fm_maxplanes[fm * 4 + res]; + fetch_start = 1 << fetchstarts[fm * 4 + res]; + cycle_sequence = cycle_sequences[max_planes - 1]; + max_planes = 1 << max_planes; + for (planes = 0; planes <= 8; planes++) { + freecycles = 0; + for (cycle = 0; cycle < 32; cycle++) + cycle_diagram_table[fm][res][planes][cycle] = -1; + if (planes <= max_planes) { + for (cycle = 0; cycle < fetch_start; cycle++) { + if (cycle < max_planes && planes >= cycle_sequence[cycle & 7]) { + v = cycle_sequence[cycle & 7]; + } else { + v = 0; + freecycles++; + } + cycle_diagram_table[fm][res][planes][cycle] = v; + } + } + cycle_diagram_free_cycles[fm][res][planes] = freecycles; + cycle_diagram_total_cycles[fm][res][planes] = fetch_start; + rplanes = planes; + if (rplanes > max_planes) + rplanes = 0; + if (rplanes == 7 && fm == 0 && res == 0) //&& !(currprefs.chipset_mask & CSMASK_AGA)) + rplanes = 4; + real_bitplane_number[fm][res][planes] = rplanes; + } + } + } + } + + //var estimated_last_fetch_cycle = 0; + //var cycle_diagram_shift = 0; + + /*this.estimate_last_fetch_cycle = function (hpos) { + var fetchunit = fetchunits[fetchmode * 4 + bplcon0_res]; + + if (plf_state < plf_passed_stop) { + var stop = plfstop < hpos || plfstop > HARD_DDF_STOP ? HARD_DDF_STOP : plfstop; + var fetch_cycle_at_stop = fetch_cycle + (stop - hpos); + var starting_last_block_at = (fetch_cycle_at_stop + fetchunit - 1) & ~(fetchunit - 1); + + estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit; + } else { + var starting_last_block_at = (fetch_cycle + fetchunit - 1) & ~(fetchunit - 1); + if (plf_state == plf_passed_stop2) + starting_last_block_at -= fetchunit; + + estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit; + } + }*/ + + const RES_LORES = 0; + const RES_HIRES = 1; + const RES_SUPERHIRES = 2; + + this.GET_RES_DENISE = function (con0) { + //if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE)) + con0 &= ~0x40; // SUPERHIRES + return (con0 & 0x8000) ? RES_HIRES : (con0 & 0x40) ? RES_SUPERHIRES : RES_LORES; + } + this.GET_RES_AGNUS = function (con0) { + //if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + con0 &= ~0x40; // SUPERHIRES + return (con0 & 0x8000) ? RES_HIRES : (con0 & 0x40) ? RES_SUPERHIRES : RES_LORES; + } + this.GET_PLANES = function (con0) { + if ((con0 & 0x0010) && (con0 & 0x7000)) + return 0; // >8 planes = 0 planes + if (con0 & 0x0010) + return 8; // AGA 8-planes bit + return (con0 >> 12) & 7; // normal planes bits + } + + this.GET_PLANES_LIMIT = function (con0) { + var res = this.GET_RES_AGNUS (con0); + var planes = this.GET_PLANES (con0); + return real_bitplane_number[fetchmode][res][planes]; + } + + + this.setup_fmodes = function (hpos) { + switch (fmode & 3) { + case 0: + fetchmode = 0; + break; + case 1: + case 2: + fetchmode = 1; + break; + case 3: + fetchmode = 2; + break; + } + //badmode = GET_RES_AGNUS (bplcon0) != GET_RES_DENISE (this.bplcon0); + bplcon0_res = this.GET_RES_AGNUS (this.bplcon0); + bplcon0_planes = this.GET_PLANES (this.bplcon0); + bplcon0_planes_limit = this.GET_PLANES_LIMIT (this.bplcon0); + //console.log(bplcon0_res, bplcon0_planes, bplcon0_planes_limit); + fetchunit = fetchunits[fetchmode * 4 + bplcon0_res]; + fetchunit_mask = fetchunit - 1; + fetchstart_shift = fetchstarts[fetchmode * 4 + bplcon0_res]; + fetchstart = 1 << fetchstart_shift; + fetchstart_mask = fetchstart - 1; + /*fm_maxplane_shift = fm_maxplanes[fetchmode * 4 + bplcon0_res]; + fm_maxplane = 1 << fm_maxplane_shift; + fetch_modulo_cycle = fetchunit - fetchstart; + if (is_bitplane_dma (hpos - 1)) cycle_line[hpos - 1] = 1;*/ + curr_diagram = cycle_diagram_table[fetchmode][bplcon0_res][bplcon0_planes_limit]; + //console.log(curr_diagram); + //console.log(fetchstart_mask); + //estimate_last_fetch_cycle (hpos); + //if (bpldmasetuphpos >= 0 && debug_dma) record_dma_event (DMA_EVENT_BPLFETCHUPDATE, hpos, vpos); + /*bpldmasetuphpos = -1; + bpldmasetupphase = 0; + ddf_change = vpos;*/ + //console.log('setup_fmodes()', fetchstart, fetchstart_shift, fetchstart_mask, curr_diagram); + } + + this.is_bitplane_dma = function (hpos) { + if (hpos < this.state.dstart*2) //plfstrt) + return 0; + //if ((plf_state == plf_end && hpos >= thisline_decision.plfright) || hpos >= estimated_last_fetch_cycle) + if (hpos >= this.state.dstop*2) //|| hpos >= estimated_last_fetch_cycle) + return 0; + + //console.log(hpos - this.state.hstart); + //return curr_diagram[(hpos - cycle_diagram_shift) & fetchstart_mask]; + return curr_diagram[hpos & fetchstart_mask]; + } + + this.get_data = function() { + return [ + this.state.hstart, + this.state.hstop - (16 << fetchmode), + cycle_diagram_total_cycles[fetchmode][this.GET_RES_AGNUS(this.bplcon0)][this.GET_PLANES_LIMIT(this.bplcon0)], + cycle_diagram_free_cycles[fetchmode][this.GET_RES_AGNUS(this.bplcon0)][this.GET_PLANES_LIMIT(this.bplcon0)] + ] + } + +} diff --git a/sae/rtc.js b/sae/rtc.js new file mode 100644 index 0000000..45e841d --- /dev/null +++ b/sae/rtc.js @@ -0,0 +1,191 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function RTC() { + const RF5C01A_RAM_SIZE = 16; + + var clock_control_d; + var clock_control_e; + var clock_control_f; + + var rtc_memory = null; + var rtc_alarm = null; + + this.read = function() { + /*struct zfile *f; + f = zfile_fopen (currprefs.flashfile, "rb", ZFD_NORMAL); + if (f) { + zfile_fread (rtc_memory, RF5C01A_RAM_SIZE, 1, f); + zfile_fread (rtc_alarm, RF5C01A_RAM_SIZE, 1, f); + zfile_fclose (f); + }*/ + } + this.write = function() { + /*struct zfile *f = zfile_fopen (currprefs.flashfile, L"rb+", ZFD_NORMAL); + if (!f) { + f = zfile_fopen (currprefs.flashfile, L"wb", 0); + if (f) { + zfile_fwrite (rtc_memory, RF5C01A_RAM_SIZE, 1, f); + zfile_fwrite (rtc_alarm, RF5C01A_RAM_SIZE, 1, f); + zfile_fclose (f); + } + return; + } + zfile_fseek (f, 0, SEEK_END); + if (zfile_ftell (f) <= 2 * RF5C01A_RAM_SIZE) { + zfile_fseek (f, 0, SEEK_SET); + zfile_fwrite (rtc_memory, RF5C01A_RAM_SIZE, 1, f); + zfile_fwrite (rtc_alarm, RF5C01A_RAM_SIZE, 1, f); + } + zfile_fclose (f);*/ + } + + this.setup = function() { + BUG.info('RTC.setup() type ' + (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_MSM6242B ? 'MSM6242B' : 'RF5C01A')); + + if (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_MSM6242B) { + clock_control_d = 0x1; + clock_control_e = 0; + clock_control_f = 0x4; /* 24/12 */ + } else if (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_RF5C01A) { + clock_control_d = 0x4; /* Timer EN */ + clock_control_e = 0; + clock_control_f = 0; + + rtc_memory = new Uint8Array(RF5C01A_RAM_SIZE); + rtc_alarm = new Uint8Array(RF5C01A_RAM_SIZE); + + for (var i = 0; i < RF5C01A_RAM_SIZE; i++) + rtc_memory[i] = rtc_alarm[i] = 0; + + this.read(); + } + } + + this.load8 = function (addr) { + addr &= 0x3f; + if ((addr & 3) == 2 || (addr & 3) == 0 || AMIGA.config.rtc.type == SAEV_Config_RTC_Type_None) { + if (AMIGA.config.cpu.model == 68000 && AMIGA.config.cpu.compatible) + return 0xff; //regs.irc >> 8; + return 0; + } + var t = new Date(); + + addr >>= 2; + if (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_MSM6242B) { + switch (addr) { + case 0x0: return t.getSeconds() % 10; + case 0x1: return Math.floor(t.getSeconds() / 10); + case 0x2: return t.getMinutes() % 10; + case 0x3: return Math.floor(t.getMinutes() / 10); + case 0x4: return t.getHours() % 10; + case 0x5: return Math.floor(t.getHours() / 10); + case 0x6: return t.getDate() % 10; + case 0x7: return Math.floor(t.getDate() / 10); + case 0x8: return (t.getMonth() + 1) % 10; + case 0x9: return Math.floor((t.getMonth() + 1) / 10); + case 0xA: return (t.getFullYear() - 1900) % 10; + case 0xB: return Math.floor((t.getFullYear() - 1900) / 10); + case 0xC: return t.getDay(); + case 0xD: return clock_control_d; + case 0xE: return clock_control_e; + case 0xF: return clock_control_f; + } + } else if (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_RF5C01A) { + var bank = clock_control_d & 3; + + if (bank >= 2 && addr < 0x0d) return (rtc_memory[addr] >> ((bank == 2) ? 0 : 4)) & 0x0f; + if (bank == 1 && addr < 0x0d) return rtc_alarm[addr]; + + switch (addr) { + case 0x0: return t.getSeconds() % 10; + case 0x1: return Math.floor(t.getSeconds() / 10); + case 0x2: return t.getMinutes() % 10; + case 0x3: return Math.floor(t.getMinutes() / 10); + case 0x4: return t.getHours() % 10; + case 0x5: return Math.floor(t.getHours() / 10); + case 0x6: return t.getDate() % 10; + case 0x7: return Math.floor(t.getDate() / 10); + case 0x8: return (t.getMonth() + 1) % 10; + case 0x9: return Math.floor((t.getMonth() + 1) / 10); + case 0xA: return (t.getFullYear() - 1900) % 10; + case 0xB: return Math.floor((t.getFullYear() - 1900) / 10); + case 0xC: return t.getDay(); + case 0xD: return clock_control_d; + /* E and F = write-only */ + } + } + return 0; + } + + this.load16 = function (addr) { + return (this.load8(addr) << 8) | this.load8(addr + 1); + } + + this.load32 = function (addr) { + return ((this.load16(addr) << 16) | this.load16(addr + 2)) >>> 0; + } + + this.store8 = function (addr, value) { + addr &= 0x3f; + if ((addr & 1) != 1 || AMIGA.config.rtc.type == SAEV_Config_RTC_Type_None) return; + + addr >>= 2; + value &= 0x0f; + if (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_MSM6242B) { + switch (addr) { + case 0xD: clock_control_d = value & (1 | 8); break; + case 0xE: clock_control_e = value; break; + case 0xF: clock_control_f = value; break; + } + } else if (AMIGA.config.rtc.type == SAEV_Config_RTC_Type_RF5C01A) { + var bank = clock_control_d & 3; + + if (bank >= 2 && addr < 0x0d) { + rtc_memory[addr] &= ((bank == 2) ? 0xf0 : 0x0f); + rtc_memory[addr] |= value << ((bank == 2) ? 0 : 4); + + var ov = rtc_memory[addr]; + if (rtc_memory[addr] != value) this.write(); + return; + } + if (bank == 1 && addr < 0x0d) { + rtc_alarm[addr] = value; + rtc_alarm[0] = rtc_alarm[1] = rtc_alarm[9] = rtc_alarm[12] = 0; + rtc_alarm[3] &= ~0x8; + rtc_alarm[5] &= ~0xc; + rtc_alarm[6] &= ~0x8; + rtc_alarm[8] &= ~0xc; + rtc_alarm[10] &= ~0xe; + rtc_alarm[11] &= ~0xc; + + var ov = rtc_alarm[addr]; + if (rtc_alarm[addr] != value) this.write(); + return; + } + switch (addr) { + case 0xD: clock_control_d = value; break; + case 0xE: clock_control_e = value; break; + case 0xF: clock_control_f = value; break; + } + } + + } + + this.store16 = function (addr, value) { + this.store8(addr, (value >> 8) & 0xff); + this.store8(addr + 1, value & 0xff); + } + + this.store32 = function (addr, value) { + this.store16(addr, (value >>> 16) & 0xffff); + this.store16(addr + 2, value & 0xffff); + } +} diff --git a/sae/serial.js b/sae/serial.js new file mode 100644 index 0000000..e898e1e --- /dev/null +++ b/sae/serial.js @@ -0,0 +1,86 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Serial() +{ + var buf = new Uint8Array(1024); + var pos = 0, dtr = false; + var serper = 0, serdat = 0x2000; + + this.reset = function() + { + this.flushBuffer(); + + pos = 0; + dtr = false; + serper = 0; + serdat = 0x2000; + } + + this.flushBuffer = function() + { + if (pos > 0) { + var str = '' + for (var i = 0; i < pos; i++) { + /*if (buf[i] == 13) + str += '
'; + else if (buf[i] == 9) + str += '   '; + else*/ + str += String.fromCharCode(buf[i]); + } + pos = 0; + BUG.col = 3; + BUG.info(str); + BUG.col = 1; + } + } + + this.readStatus = function() + { + //ciabpra |= 0x20; //Push up Carrier Detect line + //ciabpra |= 0x08; //DSR ON + return 0; + } + + this.writeStatus = function(old, nw) + { + if ((old & 0x80) == 0x80 && (nw & 0x80) == 0x00) dtr = true; + if ((old & 0x80) == 0x00 && (nw & 0x80) == 0x80) dtr = false; + //if ((old & 0x40) != (nw & 0x40)) BUG.info('RTS %s.', (nw & 0x40) == 0x40 ? 'set' : 'clr'); + //if ((old & 0x10) != (nw & 0x10)) BUG.info('CTS %s.', (nw & 0x10) == 0x10 ? 'set' : 'clr'); + return nw; + } + + this.SERPER = function(v) + { + if (serper != v) + serper = v; + } + + this.SERDAT = function(v) + { + //BUG.info('SERDAT $%04x', v); + + if (AMIGA.config.serial.enabled) { + buf[pos++] = v & 0xff; + if (pos == 1024) + this.flushBuffer(); + } + serdat |= 0x2000; /* Set TBE in the SERDATR ... */ + AMIGA.intreq |= 1; /* ... and in INTREQ register */ + } + + this.SERDATR = function() + { + //BUG.info('SERDATR $%04x', serdat); + return serdat; + } +} \ No newline at end of file diff --git a/sae/utils.js b/sae/utils.js new file mode 100644 index 0000000..3fba66f --- /dev/null +++ b/sae/utils.js @@ -0,0 +1,419 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +/* moving average algorithm */ + +function mavg(size) { + this.values = new Array(size); + this.size = size; + this.usage = 0; + this.offset = 0; + this.average = 0; + + this.clr = function() { + this.usage = 0; + this.offset = 0; + this.average = 0; + } + + this.set = function(newval) { + if (this.usage < this.size) { + this.values[this.usage++] = newval; + this.average += newval; + } else { + this.average -= this.values[this.offset]; + this.values[this.offset] = newval; + this.average += newval; + if (++this.offset >= this.size) + this.offset -= this.size; + } + return Math.floor(this.average / this.usage); + } +} + +/*-----------------------------------------------------------------------*/ + +/*function crc32(str, crc) { + const tab = + '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 '+ + '0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 '+ + '1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 '+ + '136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 '+ + '3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B '+ + '35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 '+ + '26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F '+ + '2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D '+ + '76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 '+ + '7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 '+ + '6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 '+ + '65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 '+ + '4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB '+ + '4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 '+ + '5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F '+ + '5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD '+ + 'EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 '+ + 'E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 '+ + 'F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 '+ + 'FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 '+ + 'D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B '+ + 'D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 '+ + 'CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F '+ + 'C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D '+ + '9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 '+ + '95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 '+ + '86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 '+ + '88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 '+ + 'A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB '+ + 'AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 '+ + 'BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF '+ + 'B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'; + + if (crc == window.undefined) crc = 0; + + crc = crc ^ (-1); + for (var i = 0, len = str.length; i < len; i++) + crc = (crc >>> 8) ^ parseInt(tab.substr(((crc ^ str.charCodeAt(i)) & 0xff) * 9, 8), 16); + crc = crc ^ (-1); + + return crc < 0 ? crc + 0x100000000 : crc; +}*/ + +/*-----------------------------------------------------------------------*/ +/* +* Javascript sprintf +* http://www.webtoolkit.info/ +*/ + +sprintfWrapper = { + init: function () { + if (typeof arguments == "undefined") { + return null; + } + if (arguments.length < 1) { + return null; + } + if (typeof arguments[0] != "string") { + return null; + } + if (typeof RegExp == "undefined") { + return null; + } + + var string = arguments[0]; + var exp = new RegExp(/(%([%]|(\-)?(\+|\x20)?(0)?(\d+)?(\.(\d)?)?([bcdfosxX])))/g); + var matches = new Array(); + var strings = new Array(); + var convCount = 0; + var stringPosStart = 0; + var stringPosEnd = 0; + var matchPosEnd = 0; + var newString = ''; + var match = null; + + while (match = exp.exec(string)) { + if (match[9]) { + convCount += 1; + } + + stringPosStart = matchPosEnd; + stringPosEnd = exp.lastIndex - match[0].length; + strings[strings.length] = string.substring(stringPosStart, stringPosEnd); + + matchPosEnd = exp.lastIndex; + matches[matches.length] = { + match: match[0], + left: match[3] ? true : false, + sign: match[4] || '', + pad: match[5] || ' ', + min: match[6] || 0, + precision: match[8], + code: match[9] || '%', + negative: parseInt(arguments[convCount]) < 0 ? true : false, + argument: String(arguments[convCount]) + }; + } + strings[strings.length] = string.substring(matchPosEnd); + + if (matches.length == 0) { + return string; + } + if ((arguments.length - 1) < convCount) { + return null; + } + + var code = null; + var match = null; + var i = null; + + for (i = 0; i < matches.length; i++) { + + if (matches[i].code == '%') { + substitution = '%' + } else if (matches[i].code == 'b') { + matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(2)); + substitution = sprintfWrapper.convert(matches[i], true); + } else if (matches[i].code == 'c') { + matches[i].argument = String(String.fromCharCode(parseInt(Math.abs(parseInt(matches[i].argument))))); + substitution = sprintfWrapper.convert(matches[i], true); + } else if (matches[i].code == 'd') { + matches[i].argument = String(Math.abs(parseInt(matches[i].argument))); + substitution = sprintfWrapper.convert(matches[i]); + } else if (matches[i].code == 'f') { + matches[i].argument = String(Math.abs(parseFloat(matches[i].argument)).toFixed(matches[i].precision ? matches[i].precision : 6)); + substitution = sprintfWrapper.convert(matches[i]); + } else if (matches[i].code == 'o') { + matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(8)); + substitution = sprintfWrapper.convert(matches[i]); + } else if (matches[i].code == 's') { + matches[i].argument = matches[i].argument.substring(0, matches[i].precision ? matches[i].precision : matches[i].argument.length) + substitution = sprintfWrapper.convert(matches[i], true); + } else if (matches[i].code == 'x') { + matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(16)); + substitution = sprintfWrapper.convert(matches[i]); + } else if (matches[i].code == 'X') { + matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(16)); + substitution = sprintfWrapper.convert(matches[i]).toUpperCase(); + } else { + substitution = matches[i].match; + } + + newString += strings[i]; + newString += substitution; + + } + newString += strings[i]; + + return newString; + + }, + + convert: function (match, nosign) { + if (nosign) { + match.sign = ''; + } else { + match.sign = match.negative ? '-' : match.sign; + } + var l = match.min - match.argument.length + 1 - match.sign.length; + var pad = new Array(l < 0 ? 0 : l).join(match.pad); + if (!match.left) { + if (match.pad == "0" || nosign) { + return match.sign + pad + match.argument; + } else { + return pad + match.sign + match.argument; + } + } else { + if (match.pad == "0" || nosign) { + return match.sign + match.argument + pad.replace(/0/g, ' '); + } else { + return match.sign + match.argument + pad; + } + } + } +} + +sprintf = sprintfWrapper.init; + +/*-----------------------------------------------------------------------*/ +/* + * http://www.quirksmode.org/js/detect.html + */ + +var BrowserDetect = { + init: function () { + this.browser = this.searchString(this.dataBrowser) || 'An unknown browser'; + this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || 'an unknown version'; + this.OS = this.searchString(this.dataOS) || 'an unknown OS'; + }, + searchString: function (data) { + for (var i = 0; i < data.length; i++) { + var dataString = data[i].string; + var dataProp = data[i].prop; + this.versionSearchString = data[i].versionSearch || data[i].identity; + if (dataString) { + if (dataString.indexOf(data[i].subString) != -1) return data[i].identity; + } else if (dataProp) return data[i].identity; + } + }, + searchVersion: function (dataString) { + var index = dataString.indexOf(this.versionSearchString); + if (index == -1) return; + return parseFloat(dataString.substring(index + this.versionSearchString.length + 1)); + }, + dataBrowser: [{ + string: navigator.userAgent, + subString: 'Chrome', + identity: 'Chrome' + }, { + string: navigator.userAgent, + subString: 'OmniWeb', + versionSearch: 'OmniWeb/', + identity: 'OmniWeb' + }, { + string: navigator.vendor, + subString: 'Apple', + identity: 'Safari', + versionSearch: 'Version' + }, { + prop: window.opera, + identity: 'Opera', + versionSearch: 'Version' + }, { + string: navigator.vendor, + subString: 'iCab', + identity: 'iCab' + }, { + string: navigator.vendor, + subString: 'KDE', + identity: 'Konqueror' + }, { + string: navigator.userAgent, + subString: 'Firefox', + identity: 'Firefox' + }, { + string: navigator.vendor, + subString: 'Camino', + identity: 'Camino' + }, { // for newer Netscapes (6+) + string: navigator.userAgent, + subString: 'Netscape', + identity: 'Netscape' + }, { + string: navigator.userAgent, + subString: 'MSIE', + identity: 'Explorer', + versionSearch: 'MSIE' + }, { + string: navigator.userAgent, + subString: 'Gecko', + identity: 'Mozilla', + versionSearch: 'rv' + }, { // for older Netscapes (4-) + string: navigator.userAgent, + subString: 'Mozilla', + identity: 'Netscape', + versionSearch: 'Mozilla' + }], + dataOS: [{ + string: navigator.platform, + subString: 'Win', + identity: 'Windows' + }, { + string: navigator.platform, + subString: 'Mac', + identity: 'Mac' + }, { + string: navigator.userAgent, + subString: 'iPhone', + identity: 'iPhone/iPod' + }, { + string: navigator.platform, + subString: 'Linux', + identity: 'Linux' + }] + +}; +BrowserDetect.init(); + +/*-----------------------------------------------------------------------*/ + +function dump(obj) { + var out = ''; + if (obj) { + for (var i in obj) { + out += i + ': ' + obj[i] + '\n'; + } + } else + out = 'undefined'; + + alert(out); +} + +/*-----------------------------------------------------------------------*/ + +function FatalError(err, msg) { + this.error = err; + this.message = msg; +} +FatalError.prototype = new Error; + +function Fatal(err, msg) { + //alert(str); + throw new FatalError(err, msg); +} + +/*function SafeFatal(str) { + alert(str); + console.log(str); + //API_stop(); + API({cmd:'stop'}); +}*/ + +/*-----------------------------------------------------------------------*/ + +/*function loadLocal(id, callback) { + var e = document.getElementById(id).files[0]; + var reader = new FileReader(); + reader.onload = callback; + reader.readAsBinaryString(e); +} + +function loadRemote(file, crc, callback) { + //var url = 'http://'+window.location.hostname+'/'+file; + var url = file; + + var req = new XMLHttpRequest(); + req.open('GET', url, true); + req.overrideMimeType('text\/plain; charset=x-user-defined'); + req.onreadystatechange = function(e) { + if (req.readyState == 4) { + if (req.status == 200) { + var newcrc = crc32(req.responseText, 0); + BUG.info('loadRemote() %s (length %d, crc32 $%08x)', file, req.responseText.length, newcrc); + if (newcrc == crc) + callback(req.responseText); + else + SafeFatal('Wrong checksum for file '+file); + } else + SafeFatal('Can\'t download file '+file+' (http status: '+req.status+')'); + } + } + req.send(null); +}*/ + +/*-----------------------------------------------------------------------*/ + +function Debug() { + this.col = 1; + this.on = 1; + + this.say = function (str) { + if (this.on) { + /*var e = document.createElement('span'); + e.style.color = this.col == 1 ? '#888' : (this.col == 2 ? '#448' : '#484'); + e.innerHTML = buf; + this.debug.appendChild(e); + this.debug.appendChild(document.createElement('br')); + this.debug.scrollTop = this.debug.scrollHeight;*/ + + console.log(str); + /*console.info(str); + console.warn(str); + console.error(str); + console.assert(str);*/ + } + } + + this.info = function () { + if (this.on) { + var str = sprintf.apply(this, arguments); + console.log(str); + } + } +} diff --git a/sae/video.js b/sae/video.js new file mode 100644 index 0000000..158b961 --- /dev/null +++ b/sae/video.js @@ -0,0 +1,245 @@ +/************************************************************************** +* SAE - Scripted Amiga Emulator +* +* https://github.com/naTmeg/ScriptedAmigaEmulator +* +* ©2012 Rupert Hausberger +* Commercial use is prohibited. +* +**************************************************************************/ + +function Vide0() { + const vertexShader = + 'attribute vec2 a_position;'+ + 'attribute vec2 a_texCoord;'+ + 'uniform vec2 u_resolution;'+ + 'varying vec2 v_texCoord;'+ + 'void main() {'+ + 'vec2 zeroToOne = a_position / u_resolution;'+ + 'vec2 zeroToTwo = zeroToOne * 2.0;'+ + 'vec2 clipSpace = zeroToTwo - 1.0;'+ + 'gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);'+ + 'v_texCoord = a_texCoord;'+ + '}'; + const fragmentShader = + 'precision mediump float;'+ + 'uniform sampler2D u_image;'+ + 'varying vec2 v_texCoord;'+ + 'void main() {'+ + 'gl_FragColor = texture2D(u_image, v_texCoord);'+ + '}'; + const glParams = { + alpha: false, + stencil: false, + antialias: false + }; + + var width = 0; + var height = 0; + var size = 0; + var pixels = null; + + var div = null; + var canvas = null; + var gl = null; + var video = null; + var open = false; + + /*---------------------------------*/ + + function getShader(gl, id) { + var shader, source; + + if (id == 'vertex') { + shader = gl.createShader(gl.VERTEX_SHADER); + source = vertexShader; + } else if (id == 'fragment') { + shader = gl.createShader(gl.FRAGMENT_SHADER); + source = fragmentShader; + } + gl.shaderSource(shader, source); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) + Fatal(SAEE_Video_Shader_Error, gl.getShaderInfoLog(shader)); + + return shader; + } + + function initGL() { + var vertexShader = getShader(gl, 'vertex'); + var fragmentShader = getShader(gl, 'fragment'); + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) + Fatal(SAEE_Video_Shader_Error, 'Can\'t initialise the shaders for WebGL.'); + + gl.useProgram(program); + + var positionLocation = gl.getAttribLocation(program, "a_position"); + var texCoordLocation = gl.getAttribLocation(program, "a_texCoord"); + + var texCoordBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(texCoordLocation); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + var resolutionLocation = gl.getUniformLocation(program, "u_resolution"); + gl.uniform2f(resolutionLocation, width, height); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + + gl.viewport(0, 0, width, height); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.colorMask(true, true, true, false); + } + + this.setup = function() { + if (!AMIGA.config.video.enabled) return; + if (open) this.cleanup(); + + width = AMIGA.config.video.scale ? VIDEO_WIDTH << 1 : VIDEO_WIDTH; + height = AMIGA.config.video.scale ? VIDEO_HEIGHT << 1 : VIDEO_WIDTH; + size = width * height; + //BUG.info('Video.init() %d x %d, %s mode', width, height, AMIGA.config.video.ntsc ? 'ntsc' : 'pal'); + + div = document.getElementById(AMIGA.config.video.id); + if (!div) + Fatal(SAEE_Video_ID_Not_Found, 'Video DIV-element not found. Check your code. (Malformed-DIV-name: '+AMIGA.config.video.id+')'); + + if ((canvas = document.createElement('canvas'))) { + canvas.width = width; + canvas.height = height; + } else { + if (confirm('Can\'t create a CANVAS-element. Continue without video-playback?')) + AMIGA.config.video.enabled = false; + else + Fatal(SAEE_Video_Canvas_Not_Supported, 'Can\'t create a CANVAS-element. Please update the browser.'); + } + if (AMIGA.config.video.enabled) { + if ((gl = canvas.getContext('experimental-webgl', glParams) || canvas.getContext('webgl', glParams))) + initGL(); + else + if (confirm('Can\'t initialise WebGL. Continue without video-playback?')) + AMIGA.config.video.enabled = false; + else + Fatal(SAEE_Video_WebGL_Not_Avail, 'Can\'t initialise WebGL. Is WebGL enabled in the browser-config?'); + } + if (AMIGA.config.video.enabled) { + pixels = new Uint16Array(size); + for (var i = 0; i < size; i++) pixels[i] = 0; + } + if (AMIGA.config.ports[0].type == SAEV_Config_Ports_Type_Mouse) { + canvas.onmousedown = function(e) { AMIGA.input.mouse.mousedown(e); } + canvas.onmouseup = function(e) { AMIGA.input.mouse.mouseup(e); } + canvas.onmouseover = function(e) { AMIGA.input.mouse.mouseover(e); } + canvas.onmouseout = function(e) { AMIGA.input.mouse.mouseout(e); } + canvas.onmousemove = function(e) { AMIGA.input.mouse.mousemove(e); } + } + canvas.oncontextmenu = function() { return false; } + + video = document.createElement('div'); + video.style.width = width + 'px'; + video.style.height = height + 'px'; + video.style.margin = 'auto'; + video.style.webkitTouchCallout = 'none'; + video.style.webkitUserSelect = 'none'; + video.style.khtmlUserSelect = 'none'; + video.style.mozUserSelect = 'none'; + video.style.msUserSelect = 'none'; + video.style.userSelect = 'none'; + video.appendChild(canvas); + div.appendChild(video); + open = true; + } + + this.cleanup = function() { + if (open) { + div.removeChild(video); + canvas = null; + pixels = null; + video = null; + open = false; + } + } + + /*---------------------------------*/ + + this.hideCursor = function(hide) { + canvas.style.cursor = hide ? 'none' : 'auto'; + } + + this.clear_pixels = function () { + for (var i = 0; i < size; i++) + pixels[i] = 0; + } + + this.drawpixel = function (x, y, rgb) { + pixels[y * width + x] = rgb; + } + this.draw2pixel = function (x, y, rgb) { + var ptr = y * width + x; + pixels[ptr] = pixels[ptr + 1] = rgb; + } + + this.drawline = function (y, rgb) { + var ptr = y * width; + + for (var i = 0; i < width; i++) + pixels[ptr++] = rgb; + } + this.drawline_from_to = function (x1, x2, y, rgb) { + var ptr = y * width + x1; + + for (var i = 0; i < x2 - x1; i++) + pixels[ptr++] = rgb; + } + + this.draw = function (rgb, scalex, scaley, colorOnly) { + var r = 0.0625 * (((rgb >> 11) & 31) >> 1); + var g = 0.0625 * (((rgb >> 5) & 63) >> 2); + var b = 0.0625 * ((rgb & 31) >> 1); + + gl.viewport(0, 0, width, height); + gl.clearColor(r, g, b, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + if (!colorOnly) { + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, pixels); + + var s = AMIGA.config.video.scale ? 1 : 0; + var x1 = 0; + //var x2 = 0 + (width << (scalex ? (s+1) : s)); + var x2 = 0 + (width << s); + var y1 = 0; + var y2 = 0 + (height << (scaley ? (s+1) : s)); + //var y2 = 0 + (height << s); + + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + x1, y1, x2, y1, x1, y2, + x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + } + } +}