update 0.7.0

This commit is contained in:
Rupert Hausberger 2012-12-22 19:39:33 +01:00
commit 0c5b018866
24 changed files with 21049 additions and 0 deletions

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

206
index.css Normal file
View file

@ -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;
}

627
index.htm Normal file
View file

@ -0,0 +1,627 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>SAE - Scripted Amiga Emulator</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="author" content="Rupert Hausberger" />
<meta name="publisher" content="Rupert Hausberger" />
<meta name="description" content="Experimental Amiga Classic Emulator in Javascript" />
<meta name="keywords" content="SAE,Javascript,HTML5,Amiga,AmigaOS,Emulator,Online" />
<meta name="robots" content="index,nofollow" />
<link rel="icon" type="image/x-icon" href="http://scriptedamigaemulator.net/favicon.ico" />
<link rel="stylesheet" type="text/css" href="index.css" />
<script type="text/javascript" src="sae/constants.js"></script>
<script type="text/javascript" src="sae/utils.js"></script>
<script type="text/javascript" src="sae/config.js"></script>
<script type="text/javascript" src="sae/memory.js"></script>
<script type="text/javascript" src="sae/expansion.js"></script>
<script type="text/javascript" src="sae/events.js"></script>
<script type="text/javascript" src="sae/input.js"></script>
<script type="text/javascript" src="sae/serial.js"></script>
<script type="text/javascript" src="sae/custom.js"></script>
<script type="text/javascript" src="sae/blitter.js"></script>
<script type="text/javascript" src="sae/copper.js"></script>
<script type="text/javascript" src="sae/playfield.js"></script>
<script type="text/javascript" src="sae/video.js"></script>
<script type="text/javascript" src="sae/audio.js"></script>
<script type="text/javascript" src="sae/cia.js"></script>
<script type="text/javascript" src="sae/disk.js"></script>
<script type="text/javascript" src="sae/rtc.js"></script>
<script type="text/javascript" src="sae/cpu.js"></script>
<script type="text/javascript" src="sae/amiga.js"></script>
<script type="text/javascript" src="index.js"></script>
<script type="text/javascript" src="ga.js"></script>
</head>
<body onload="init()">
<div id="base">
<div id="head" class="head">
<div style="height:8px"></div>
<img src="images/logo.png" alt="Scripted Amiga Emulator" />
<noscript>
<div class="noscript">This site does require javascript, obviously :)</div>
</noscript>
<div class="hr"></div>
</div>
<div style="height:20px"></div>
<div id="config">
<div id="config_simple">
<div class="arm">
<button class="button" onclick="switchCfg(1)">Advanced</button>
</div>
<div style="line-height:20px">
<h3>Welcome!</h3>
This site is a demonstration of the '<a href="javascript:openNewTab('https://github.com/naTmeg/ScriptedAmigaEmulator')">SAE</a>' project,
an experimental <a href="javascript:openNewTab('http://en.wikipedia.org/wiki/Amiga')">Amiga</a> emulator in pure javascript and HTML5.<br />
For details, please refer to the <a href="javascript:openNewTab('readme.htm')">readme</a> file.<br /><br />
Below you find a small collection of games and demos to play...
</div>
<div style="height:20px"></div>
<div class="config_simple_hof">
<div class="acm">
<span class="fwb">Hall of fame</span>
</div>
<table>
<tr>
<td class="armsb">Game</td>
<td>
<select id="cfg_game" size="1" onchange="preSelect(1)">
<option value="0">- select -</option>
</select>
</td>
</tr>
<tr>
<td class="armsb">Demo</td>
<td>
<select id="cfg_demo" size="1" onchange="preSelect(2)">
<option value="0">- select -</option>
</select>
</td>
</tr>
<tr>
<td class="arms">Audio</td>
<td><input id="cfg_audio_enabled_1" type="checkbox" checked="checked" /></td>
</tr>
<tr>
<td class="arms">Scale 4x</td>
<td><input id="cfg_video_scale_1" type="checkbox" /><span class="info">(1140 x 1152, press F11 for fullscreen)</span></td>
</tr>
</table>
<div class="hr"></div>
<table id="cfg_info">
<tr>
<td class="armsb">Name</td>
<td><span id="cfg_info_name"></span></td>
</tr>
<tr>
<td class="armsb">From</td>
<td><span id="cfg_info_comp"></span></td>
</tr>
<tr>
<td class="armsb">Year</td>
<td><span id="cfg_info_year"></span></td>
</tr>
<tr id="cfg_info_ctrl_grp">
<td class="armsb">Controls</td>
<td><span id="cfg_info_ctrl"></span></td>
</tr>
<tr id="cfg_info_load_grp">
<td class="armsb">Loading</td>
<td><span id="cfg_info_load"></span></td>
</tr>
</table>
<div style="height:10px"></div>
<div class="acm">
<button class="sbutton" id="cfg_simple_start" onclick="simpleStart()">Play</button>
</div>
</div>
</div>
<div id="config_advanced">
<div class="arm">
<button class="button" onclick="switchCfg(0)">Simple</button>
</div>
<table>
<tr>
<td class="armsb">CPU</td>
<td class="bl">
<table class="wauto">
<tr>
<td class="arm">Speed:</td>
<td>
<select id="cfg_cpu_speed" size="1">
<option value="0">Original</option>
<option value="-1">Maximum</option>
</select>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="armsb">RAM</td>
<td class="bl">
<table class="wauto">
<tr>
<td class="arm">Chip:</td>
<td>
<select id="cfg_mem_chip" size="1">
<option value="1">256</option>
<option value="2">512</option>
<option value="3">1024</option>
<option value="4">2048</option>
</select>
kb
</td>
</tr>
<tr>
<td class="arm">Slow:</td>
<td>
<select id="cfg_mem_slow" size="1">
<option value="0">0</option>
<option value="1">256</option>
<option value="2">512</option>
<option value="3">1024</option>
<option value="4">1536</option>
</select>
kb <span class="gray">(pseudo-fast, bogomem)</span>
</td>
</tr>
<tr>
<td class="arm">Fast:</td>
<td>
<select id="cfg_mem_fast" size="1">
<option value="0">0</option>
<option value="1">512</option>
<option value="2">1024</option>
<option value="3">2048</option>
<option value="4">4096</option>
<option value="5">8192</option>
</select>
kb <span class="gray">(real-fast, commodore a2058 in zorro2)</span>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="armsb">ROM</td>
<td class="bl">
<table id="cfg_rom_grp">
<tr>
<td class="arm">Kickstart:</td>
<td style="width:100%"><span id="cfg_rom_name" class="red">unset (required)</span></td>
<td><input id="cfg_rom_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="romSelect()" /></td>
<td><button class="button" onclick="document.getElementById('cfg_rom_file').click()">Select</button></td>
<td><button class="button" id="cfg_rom_aros" onclick="romAROS()"><span class="green">Set AROS</span></button></td>
</tr>
<tr>
<td class="arm">Extended:</td>
<td style="width:100%"><span id="cfg_ext_name" class="gray">unset (optional)</span></td>
<td><input id="cfg_ext_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="extSelect()" /></td>
<td><button class="button" onclick="document.getElementById('cfg_ext_file').click()">Select</button></td>
<td><button class="button" id="cfg_ext_remove" onclick="extRemove()">Remove</button></td>
</tr>
<tr id="cfg_ext_addr_grp">
<td class="arm">Ext. addr:</td>
<td colspan="4">
<select id="cfg_ext_addr" size="1">
<option value="2">0xE00000</option>
<option value="3">0xF00000</option>
</select>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="armsb">Floppy</td>
<td class="bl">
<table style="border-collapse:collapse">
<tr>
<td style="width:45px"><input id="cfg_df0_enabled" type="checkbox" onchange="floppyUpdate(0)" />DF0</td>
<td>
<table id="cfg_df0_grp">
<tr>
<td>
<select id="cfg_df0_type" size="1">
<option value="1">3.5" DD</option>
<option value="2">3.5" HD</option>
<option value="3">5.25" SD</option>
</select>
</td>
<td style="width:100%"><span id="cfg_df0_name" class="gray">empty (optional)</span></td>
<td><input id="cfg_df0_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="floppyInsert(0)" /></td>
<td><button class="button" onclick="document.getElementById('cfg_df0_file').click()">Insert</button></td>
<td><button class="button" id="cfg_df0_eject" onclick="floppyEject(0)">Eject</button></td>
</tr>
</table>
</td>
</tr>
<tr>
<td><input id="cfg_df1_enabled" type="checkbox" onchange="floppyUpdate(1)" />DF1</td>
<td>
<table id="cfg_df1_grp">
<tr>
<td>
<select id="cfg_df1_type" size="1">
<option value="1">3.5" DD</option>
<option value="2">3.5" HD</option>
<option value="3">5.25" SD</option>
</select>
</td>
<td style="width:100%"><span id="cfg_df1_name" class="gray">empty (optional)</span></td>
<td><input id="cfg_df1_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="floppyInsert(1)" /></td>
<td><button class="button" onclick="document.getElementById('cfg_df1_file').click()">Insert</button></td>
<td><button class="button" id="cfg_df1_eject" onclick="floppyEject(1)">Eject</button></td>
</tr>
</table>
</td>
</tr>
<tr>
<td><input id="cfg_df2_enabled" type="checkbox" onchange="floppyUpdate(2)" />DF2</td>
<td>
<table id="cfg_df2_grp">
<tr>
<td>
<select id="cfg_df2_type" size="1">
<option value="1">3.5" DD</option>
<option value="2">3.5" HD</option>
<option value="3">5.25" SD</option>
</select>
</td>
<td style="width:100%"><span id="cfg_df2_name" class="gray">empty (optional)</span></td>
<td><input id="cfg_df2_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="floppyInsert(2)" /></td>
<td><button class="button" onclick="document.getElementById('cfg_df2_file').click()">Insert</button></td>
<td><button class="button" id="cfg_df2_eject" onclick="floppyEject(2)">Eject</button></td>
</tr>
</table>
</td>
</tr>
<tr>
<td><input id="cfg_df3_enabled" type="checkbox" onchange="floppyUpdate(3)" />DF3</td>
<td>
<table id="cfg_df3_grp">
<tr>
<td>
<select id="cfg_df3_type" size="1">
<option value="1">3.5" DD</option>
<option value="2">3.5" HD</option>
<option value="3">5.25" SD</option>
</select>
</td>
<td style="width:100%"><span id="cfg_df3_name" class="gray">empty (optional)</span></td>
<td><input id="cfg_df3_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="floppyInsert(3)" /></td>
<td><button class="button" onclick="document.getElementById('cfg_df3_file').click()">Insert</button></td>
<td><button class="button" id="cfg_df3_eject" onclick="floppyEject(3)">Eject</button></td>
</tr>
</table>
</td>
</tr>
</table>
Speed:
<select id="cfg_floppy_speed" size="1">
<option value="0">Turbo</option>
<option value="100">Original</option>
<option value="200">2x</option>
<option value="500">5x</option>
<option value="1000">10x</option>
</select>
<span class="gray"> ('Turbo' mode is fastest, but not very compatible)</span>
</td>
</tr>
<tr>
<td class="armsb">Audio</td>
<td class="bl">
<input id="cfg_audio_enabled" type="checkbox" onchange="audioUpdate()" />
Enabled
<table id="cfg_audio_grp" class="wauto">
<tr>
<td class="arm">Mode:</td>
<td>
<select id="cfg_audio_mode" size="1">
<option value="0">Off (emulated)</option>
<option value="1">On</option>
<option value="2">On (best)</option>
</select>
</td>
</tr>
<tr>
<td class="arm">Channels:</td>
<td>
<select id="cfg_audio_channels" size="1">
<option value="1">1 - Mono</option>
<option value="2">2 - Stereo</option>
</select>
</td>
</tr>
<tr>
<td class="arm">Samplerate:</td>
<td>
<select id="cfg_audio_rate" size="1">
<option value="1">11025</option>
<option value="2">22050</option>
<option value="3">44100</option>
<option value="4">48000</option>
</select>
Hz
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="armsb">Video</td>
<td class="bl">
<input id="cfg_video_enabled" type="checkbox" onchange="videoUpdate()" />
Enabled
<div id="cfg_video_grp">
<input id="cfg_chipset_cl_enabled" type="checkbox" onchange="spritesUpdate()" />
Sprite collisions
<span id="cfg_chipset_cl_grp">
<select id="cfg_chipset_cl" size="1">
<option value="1">Sprite/Sprite</option>
<option value="2">Sprite/Sprite + Sprite/Playfield</option>
</select>
</span>
<br />
<input id="cfg_video_scale" type="checkbox" />
Scale 4x <span class="gray">(1440 x 1152)</span>
<br />
<input id="cfg_video_ntsc" type="checkbox" />
NTSC
<br />
<input id="cfg_video_skip" type="checkbox" />
Frameskip<span class="gray"> (if the emulation is too slow, up to 4 frames will be skipped)</span>
<!--<br />
<input id="cfg_video_drop" type="checkbox" />
Framedrop<span class="gray"> (if the emulation is too fast, a (unprecise) delay is initiated)</span>-->
</div>
</td>
</tr>
<tr>
<td class="armsb">Keyboard</td>
<td class="bl">
<input id="cfg_keyborad_enabled" type="checkbox" onchange="keyboradUpdate()" />
Enabled <span class="gray">(joysticks does always work)</span>
<div id="cfg_keyborad_grp">
<input id="cfg_keyborad_mapshift" type="checkbox" />
Map L/R-Shift to L/R-Arrows <span class="gray">(team17 pinball games)</span>
</div>
</td>
</tr>
<tr>
<td class="armsb">Port 0</td>
<td>
<input id="cfg_ports_0_enabled" type="checkbox" onchange="portUpdate(0)" />
Enabled
<span id="cfg_ports_0_grp">
<select id="cfg_ports_0" size="1" onchange="portUpdate2()">
<option value="1">Mouse</option>
<option value="2">Joystick 0</option>
</select>
</span>
<span id="cfg_ports_0_grp2">
Move
<select id="cfg_ports_0_move" size="1">
<option value="1">Arrows</option>
<option value="2">Numpad</option>
<option value="3">WASD</option>
</select>
Fire 1
<select id="cfg_ports_0_fire_1" size="1">
<option value="0">None</option>
<option value="16">Shift</option>
<option value="17">Ctrl</option>
<option value="13">Enter</option>
<option value="32">Space</option>
<option value="8">Backspace</option>
<option value="96">Numpad 0</option>
<option value="106">Numpad *</option>
<option value="107">Numpad </option>
<option value="109">Numpad -</option>
<option value="110">Numpad .</option>
<option value="111">Numpad /</option>
<option value="46">Delete</option>
<option value="45">Insert</option>
<option value="34">Page down</option>
<option value="33">Page up</option>
<option value="35">End</option>
<option value="36">Home</option>
<option value="19">Pause</option>
<option value="144">Num lock</option>
<option value="145">Scroll lock</option>
<option value="49">1</option>
<option value="50">2</option>
<!--<option value="122">F11</option>
<option value="123">F12</option>
<option value="186">Semi-colon</option>
<option value="187">Equal</option>
<option value="188">Comma</option>
<option value="189">Dash</option>
<option value="190">Period</option>
<option value="222">Quote</option>
<option value="226">Less greater </option>
<option value="191">Forward slash</option>
<option value="220">Back slash</option>
<option value="219">Open bracket</option>
<option value="221">Close bracket</option>
<option value="192">Grave accent</option>-->
</select>
Fire 2
<select id="cfg_ports_0_fire_2" size="1">
<option value="0">None</option>
<option value="16">Shift</option>
<option value="17">Ctrl</option>
<option value="13">Enter</option>
<option value="32">Space</option>
<option value="8">Backspace</option>
<option value="96">Numpad 0</option>
<option value="106">Numpad *</option>
<option value="107">Numpad </option>
<option value="109">Numpad -</option>
<option value="110">Numpad .</option>
<option value="111">Numpad /</option>
<option value="46">Delete</option>
<option value="45">Insert</option>
<option value="34">Page down</option>
<option value="33">Page up</option>
<option value="35">End</option>
<option value="36">Home</option>
<option value="19">Pause</option>
<option value="144">Num lock</option>
<option value="145">Scroll lock</option>
<option value="49">1</option>
<option value="50">2</option>
</select>
</span>
</td>
</tr>
<tr>
<td class="armsb">Port 1</td>
<td class="bl">
<input id="cfg_ports_1_enabled" type="checkbox" onchange="portUpdate(1)" />
Enabled
<span id="cfg_ports_1_grp">
<select id="cfg_ports_1" size="1" onchange="portsUpdate(1)">
<option value="3">Joystick 1</option>
</select>
Move
<select id="cfg_ports_1_move" size="1">
<option value="1">Arrows</option>
<option value="2">Numpad</option>
<option value="3">WASD</option>
</select>
Fire 1
<select id="cfg_ports_1_fire_1" size="1">
<option value="0">None</option>
<option value="16">Shift</option>
<option value="17">Ctrl</option>
<option value="13">Enter</option>
<option value="32">Space</option>
<option value="8">Backspace</option>
<option value="96">Numpad 0</option>
<option value="106">Numpad *</option>
<option value="107">Numpad </option>
<option value="109">Numpad -</option>
<option value="110">Numpad .</option>
<option value="111">Numpad /</option>
<option value="46">Delete</option>
<option value="45">Insert</option>
<option value="34">Page down</option>
<option value="33">Page up</option>
<option value="35">End</option>
<option value="36">Home</option>
<option value="19">Pause</option>
<option value="144">Num lock</option>
<option value="145">Scroll lock</option>
<option value="123">F12</option>
</select>
Fire 2
<select id="cfg_ports_1_fire_2" size="1">
<option value="0">None</option>
<option value="16">Shift</option>
<option value="17">Ctrl</option>
<option value="13">Enter</option>
<option value="32">Space</option>
<option value="8">Backspace</option>
<option value="96">Numpad 0</option>
<option value="106">Numpad *</option>
<option value="107">Numpad </option>
<option value="109">Numpad -</option>
<option value="110">Numpad .</option>
<option value="111">Numpad /</option>
<option value="46">Delete</option>
<option value="45">Insert</option>
<option value="34">Page down</option>
<option value="33">Page up</option>
<option value="35">End</option>
<option value="36">Home</option>
<option value="19">Pause</option>
<option value="144">Num lock</option>
<option value="145">Scroll lock</option>
<option value="123">F12</option>
</select>
</span>
</td>
</tr>
<tr>
<td class="armsb">Serial</td>
<td>
<input id="cfg_serial_enabled" type="checkbox" />
Enabled
</td>
</tr>
</table>
<div style="height:10px"></div>
<div class="acm">
<button class="sbutton" id="cfg_start" onclick="advandedStart()">Start</button>
</div>
</div>
</div>
<div style="height:10px"></div>
<div id="foot" class="foot">
<span class="gray">&#169;2012 Rupert Hausberger</span> |
<a href="javascript:openNewTab('https://github.com/naTmeg/ScriptedAmigaEmulator')">Source</a> |
<a href="javascript:openNewTab('readme.htm')">Readme</a>
</div>
</div>
<div id="emul">
<div id="status">
<table>
<tr>
<td><button class="dbutton" onclick="stop()">Stop</button></td>
<!--<td><button class="dbutton" onclick="reset()">Reset</button></td>-->
<td><button class="dbutton" onclick="pause(1)" id="status_pr">Pause</button></td>
<td><button id="dskchg_grp" class="dbutton" onclick="dskchgOpen()">Disk</button></td>
<!--<td><button class="dbutton" onclick="toggleFullScreen()">fullscreen</button></td>-->
<td class="arm" style="width:32px"><span id="led_pwr">pwr</span></td>
<td class="arm" style="width:32px"><span id="led_df0">df0</span></td>
<td class="arm" style="width:32px"><span id="led_df1">df1</span></td>
<td class="arm" style="width:32px"><span id="led_df2">df2</span></td>
<td class="arm" style="width:32px"><span id="led_df3">df3</span></td>
<td class="arm" style="width:32px"><span id="led_fps">fps</span></td>
<td class="arm" style="width:32px"><span id="led_cpu">cpu</span></td>
</tr>
</table>
</div>
<div id="dskchg_simple">
<table>
<tr>
<td>
<select id="cfg_dskchg_select" size="1" onchange="dskchgSelect()">
<option value="">- select -</option>
</select>
</td>
<td><button class="dbutton" onclick="dskchgClose()">Abort</button></td>
</tr>
</table>
</div>
<div id="dskchg">
<table>
<tr>
<td>
<select id="cfg_dskchg_unit" size="1">
<option value="0">DF0</option>
<option value="1">DF1</option>
<option value="2">DF2</option>
<option value="3">DF3</option>
</select>
</td>
<td><input id="cfg_dskchg_file" type="file" style="position:absolute;left:-10000px;top:-10000px;" onchange="dskchgInsert()" /></td>
<td><button class="dbutton" onclick="document.getElementById('cfg_dskchg_file').click()">Insert</button></td>
<td><button class="dbutton" onclick="dskchgEject()">Eject</button></td>
<td><button class="dbutton" onclick="dskchgClose()">Abort</button></td>
</tr>
</table>
</div>
<div style="height:10px"></div>
<div id="myVideo"></div>
</div>
</body>
</html>

1475
index.js Normal file

File diff suppressed because it is too large Load diff

388
readme.htm Normal file
View file

@ -0,0 +1,388 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>SAE - Scripted Amiga Emulator</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="icon" type="image/x-icon" href="http://scriptedamigaemulator.net/favicon.ico" />
<link rel="stylesheet" type="text/css" href="index.css" />
<script type="text/javascript">
function openNewTab(url) {
window.open(url, '_blank');
window.focus();
}
</script>
</head>
<body>
<div class="head">
<div style="height:8px"></div>
<img src="images/logo.png" alt="Scripted Amiga Emulator" />
<noscript>
<div class="noscript">This site does require javascript, obviously :)</div>
</noscript>
<div class="hr"></div>
</div>
<div style="height:20px"></div>
<div class="readme">
<table>
<tr>
<td>
<ul>
<li><span class="fwb">Requirements</span>
<ul>
<li>A very fast computer</li>
<li>A browser capable of HTML5 with:
<ul>
<li>WegGL</li>
<li>WebAudio</li>
<li>Typed arrays</li>
<li>FileReader (GUI)</li>
</ul>
</li>
<li>A ROM:
<ul>
<li>Amiga Kickstart 1.x-2.04 (not included for copyright reasons)</li>
<li>AROS Kickstart (included)</li>
</ul>
</li>
<li style="list-style-type:none"><br /></li>
<li>And a bunch of ADF-files to test (optional)</li>
</ul>
</li>
</ul>
</td>
<td>
<table>
<tr><td colspan="2"><span class="fwb">Supported browsers</span></td></tr>
<tr><td class="armb" style="color:green">Chrome</td><td>Fastest</td></tr>
<tr><td class="armb" style="color:green">Firefox</td><td>Medium speed</td></tr>
<tr><td class="armb" style="color:orange">Opera</td><td>Slow speed, no audio</td></tr>
<tr><td class="armb" style="color:red">IE</td><td>No support</td></tr>
<tr><td class="armb">Other</td><td>Untested</td></tr>
</table>
</td>
</tr>
</table>
<div class="hr"></div>
<ul>
<li><span class="fwb">Goals</span>
<ul>
<li>Make Amiga Classic Emulation available for browsers.</li>
<li>Easy integration into websites.</li>
<li>API for control from background, useful e.g. for a demo-player.</li>
<li>Browsers-plugins should not be required, just HTML5 and javascript.</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Possible usage</span>
<ul>
<li>Demo/Intro/Mod-player</li>
<li>Playing games</li>
<li>Browser benchmark</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Usage/Hints</span>
<ul>
<li>Press F11 for fullscreen. Most browsers support that.</li>
<li>If something does crash, try another memory setting or a different ROM. The original kickstart (1.3) work's best.</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Known bugs/problems</span>
<ul>
<li>Fast blinking sprites are not displayed if 'Frameskip' is active.</li>
<li>Copper initiated color-changes within a scanline are not recognized.</li>
<li>Left/right shift/alt/ctrl are the same. That's a browser 'feature' and can't be fixed for now.</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Missing features</span>
<ul>
<li>User-config</li>
<li>Maybe ECS support</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Written and ported by</span>
<ul>
<li>Rupert Hausberger &lt;<a href="mailto:rupert_hausberger@gmx.net">rupert_hausberger@gmx.net</a>&gt; (<a href="javascript:openNewTab('https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=ZJA3YVT6EP55A')">donate</a>)</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Source code</span>
<ul>
<li><a href="javascript:openNewTab('https://github.com/naTmeg/ScriptedAmigaEmulator')">github.com/naTmeg/ScriptedAmigaEmulator</a></li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">Thanks</span>
<ul>
<li>Bernd Schmidt</li>
<li>WinUAE and AROS team.</li>
</ul>
</li>
</ul>
<ul>
<li><span class="fwb">License</span>
<ul>
<li>This is a <b>free</b> project.</li>
<li>You may use the emulator in your own website.</li>
<li>If you use the emulator in your own website, you <b>must</b> provide a link to this homepage.</li>
<li>If you use the emulator in your own website, you <b>must not</b> change the copyright header in the source-files.</li>
<li>Commercial use <b>in any form</b> is prohibited. This includes donations.</li>
<li>No warranty expressed or implied. Use at your own risk.</li>
</ul>
</li>
</ul>
<div class="hr"></div>
<table>
<tr><td colspan="3" class="acm"><span class="fwb">Fully working...</span></td></tr>
<tr>
<td>
<ul>
<li><span class="fwb">Games</span>
<ul>
<li>Alien Breed</li>
<li>Alien Breed Special Edition 92<span class="fwb"> (0.7)</span></li>
<li>Alien Breed Tower Assault<span class="fwb"> (0.6)</span></li>
<li>Alien Breed II - The horror continues<span class="fwb"> (0.7)</span></li>
<li>Boby Blows Galactic<span class="fwb"> (0.7)</span></li>
<li>Agony<span class="fwb"> (0.6.1)</span></li>
<li>Another World</li>
<li>Barbarian<span class="fwb"> (0.7)</span></li>
<li>Battle Chess</li>
<li>Blood Money</li>
<li>Crystal Hammer</li>
<li>Dynablaster<span class="fwb"> (0.7)</span></li>
<li>Firepower</li>
<li>Lotus Turbo Challenge 1/2<span class="fwb"> (0.6)</span></li>
<li>NorthPrince of Persia<span class="fwb"> (0.7)</span></li>
<li>Pinball Dreams</li>
<li>Pinball Fantasies (the demo with 1 desk)</li>
<li>Prince of Persia<span class="fwb"> (0.7)</span></li>
<li>Project-X<span class="fwb"> (0.7)</span></li>
<li>Project-X Special Edition '92<span class="fwb"> (0.6)</span></li>
<li>P.O.W.</li>
<li>Rubicon<span class="fwb"> (0.6)</span></li>
<li>R-Type 2</li>
<li>Shadow of the Beast 2<span class="fwb"> (0.6)</span></li>
<li>Terminator 2<span class="fwb"> (0.7)</span></li>
<li>Turrican 1/2</li>
<li>Turrican 3<span class="fwb"> (0.6)</span></li>
<li>Wings<span class="fwb"> (0.6)</span></li>
<li>Wings of Death<span class="fwb"> (0.6.1)</span></li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li><span class="fwb">Demos</span>
<ul>
<li>Deja Vu</li>
<li>How to skin a cat</li>
<li>Project-X (demo-rolling)<span class="fwb"> (0.6)</span></li>
<li>State of the Art</li>
</ul>
</li>
</ul>
</td>
<td>
<ul>
<li><span class="fwb">Apps</span>
<ul>
<li>Devpak 2.14</li>
<li>DPaint 4.1</li>
<li>Protracker 3.61</li>
<li>SysInfo 3.24</li>
<li>Workbench 1.x</li>
<li>Workbench 2.04<span class="fwb"> (0.6)</span></li>
</ul>
</li>
</ul>
</td>
</tr>
<tr><td colspan="3" class="acm"><span class="fwb">Note:</span> The lists corresponds to Kickstart 1.3</td></tr>
</table>
<div class="hr"></div>
<ul>
<li><span class="fwb">History</span>
<ul>
<li>0.7.0 (24.12.2012):
<ul>
<li>Common
<ul>
<li>Chaned the project-name from 'Janus' to 'SAE' (Scripted Amiga Emulator).<br />
This step is necessary because i did'nt enough research before the initial release.<br />
There is already a project with that name in it. (<a href="javascript:openNewTab('http://janus-uae.sourceforge.net')">janus-uae</a>)
I'm sorry for that, that was kinda stupid :)</li>
<li>Separated the GUI form the core and rewrote most of it.</li>
<li>Simplified the whole code. This means, that functions are called faster.</li>
<li>Ported new Copper-, Blitter-, Audio-, Disk- and event-handling code from WinUAE 2.5.0</li>
<li>Moved all "beam-functions" to events.js, for faster access.</li>
<li>Added "CPU-usage" to the GUI.</li>
<li>Added "Hall of Fame".</li>
</ul>
</li>
<li>CPU
<ul>
<li>Added support for exact cycling. The CPU have now 7.09 MHz on PAL and 7.16 MHz on NTSC resp.</li>
<li>Added option for "Original"- and "Maximum"-speed.</li>
</ul>
</li>
<li>Audio
<ul>
<li>Added a ring-buffer before output. No more "bagpipe"-like audio.</li>
<li>Added a option to just emulate the hardware, but not to play any sound, e.g. "Agony" was not starting, if audio was disabled.</li>
</ul>
</li>
<li>Display
<ul>
<li>Switched from the RGB to the 565 texture-format-encoding in WebGL, which gives a little more speed.</li>
<li>Added support and a option for 4x scaling.</li>
<li>Fixed screen-draging in lo-/hires-modes.</li>
<li>Added a option to disable the output. Can be used to better play music-demos.</li>
</ul>
</li>
<li>Disk
<ul>
<li>Added support for different speed-modes.</li>
<li>Added support for extended ADF-files.</li>
<li>Enabled write support. (to memory)</li>
</ul>
</li>
<li>Input
<ul>
<li>The F-keys does work normaly now, i.e F5 does'nt reload the emulator, backspace does'nt go to the last page, etc...</li>
<li>Fixed bug in the joystick-emulation.</li>
</ul>
</li>
</ul>
</li>
<li style="list-style-type:none"><br/></li>
<li>0.6.1 (05.11.2012):
<ul>
<li>CPU
<ul>
<li>Added missing TAS command. ('Wings of Death' game does use it)</li>
</ul>
</li>
<li>ROM
<ul>
<li>Added a crc32-checksum test after download. (AROS)</li>
<li>Changed transfer-mode from syncronous to asyncronous. (AROS)</li>
</ul>
</li>
<li>Display
<ul>
<li>Optimized the line-drawing code of the blitter.</li>
<li>Fixed decision whether the resulting image is scaled or not. ('Agony' game)</li>
<li>Disabled 'Framedrop' in interlace-mode.</li>
</ul>
</li>
<li>Misc
<ul>
<li>Fixed HTML5 doctype.</li>
<li>Added ver/rev to the title.</li>
<li>Changed revision to 3 digits.</li>
</ul>
</li>
</ul>
</li>
<li style="list-style-type:none"><br/></li>
<li>0.6 (25.10.2012):
<ul>
<li>ROM
<ul>
<li>Added support for extended-roms.</li>
<li>AROS kickstart replacement is now the default-rom, and will be downloaded and used on the fly.</li>
</ul>
</li>
<li>RAM
<ul>
<li>Changed internal memory alignment from 8 to 16 bit.</li>
<li>Added fast-ram support, through a emulated Commodore A2058 in Zorro2-mode.</li>
<li>Removed the D8DC-memory-option from the GUI. This memory space is now always allocated.</li>
<li>Sorted access of the memory-routines by priority.</li>
</ul>
</li>
<li>CPU
<ul>
<li>Fixed sign-extension for memory to address-register transfers in MOVEM.</li>
<li>Fixed sign-extension for absolute-word addresses in exEA.</li>
<li>Fixed wrong address when writing a byte to the stack in ldEA.</li>
<li>Fixed 32bit calculations for address-regsiters in ADDQ/SUBQ.</li>
<li>Fixed remainder calculation in DIVU.</li>
<li>Optimized integer-arithmetic functions. Size depending calcalations are now used.</li>
<li>Improved exception 2/3 handling.</li>
<li>More small cleanups not listed here.</li>
</ul>
</li>
<li>Disk
<ul>
<li>Ported latest disk-code from WinUAE. Disk-based problems should be gone now, e.g. floopy shown as BAD or read errors.</li>
<li>Diskchange does now work.</li>
<li>Drives can be disabled now.</li>
<li>DD/HD types are now supported.</li>
</ul>
</li>
<li>Display
<ul>
<li>Added missing support for HAM6.</li>
<li>Fixed horizontal and vertical screen-centering.</li>
<li>Fixed screen-draging, almost, read the "Known bugs" section.</li>
<li>Added better screen-scrolling.</li>
</ul>
</li>
<li>Input
<ul>
<li>Fixed key-mapping to the amiga key-layout, at least in Chrome. Firefox does not like "öäü", it seems.</li>
<li>Added support for individual configurations of joystick-movement and fire-buttons.</li>
<li>Fixed bug, that made the fire 1 button on joystick 0 not working.</li>
<li>Fixed bug in the mouse-movement function, that calculates the wrong coordinates in some cases.</li>
</ul>
</li>
<li>GUI
<ul>
<li>Added eject-button in the disk-change requester.</li>
<li>Removed the CPU and OCS/ECS options. They are useless so far.</li>
<li>The "Pause" and "Resume" buttons are once now.</li>
<li>Made some options to only apear when selected.</li>
<li>Did some cosmetics.</li>
</ul>
</li>
<li>Misc
<ul>
<li>Added support for Zorro2-expansion cards.</li>
<li>Added a more precise event-handling. This makes the audio-output a little smoother.</li>
<li>Changed the ratio of CPU-/Chipset-time from 1:8 to 1:4</li>
<li>Fixed a bug in the RTC. "Year" is now calculated correctly.</li>
<li>Fixed incorrect response of serial.device. Now crashed programms does reset (and show the guru), instead of idle.</li>
<li>Any output on the serial.device is now redirected to the debug-log. (dev only)</li>
<li>Kickstart/Workbench 2.04 and AROS does now work.</li>
<li>Overall about 10-30% more speed, as long as the blitter is not used too much.</li>
</ul>
</li>
</ul>
</li>
<li style="list-style-type:none"><br/></li>
<li>0.5 (01.09.2012):
<ul>
<li>Initial.</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<div style="height:20px"></div>
</body>
</html>

459
sae/amiga.js Normal file
View file

@ -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:'' };
}

1126
sae/audio.js Normal file

File diff suppressed because it is too large Load diff

2904
sae/blitter.js Normal file

File diff suppressed because it is too large Load diff

738
sae/cia.js Normal file
View file

@ -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);
}
}

188
sae/config.js Normal file
View file

@ -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) {}
}

317
sae/constants.js Normal file
View file

@ -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;

615
sae/copper.js Normal file
View file

@ -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);
}
}
}
}

5232
sae/cpu.js Normal file

File diff suppressed because it is too large Load diff

350
sae/custom.js Normal file
View file

@ -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);
}
}

1674
sae/disk.js Normal file

File diff suppressed because it is too large Load diff

803
sae/events.js Normal file
View file

@ -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);
}
}

177
sae/expansion.js Normal file
View file

@ -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);
}
}
}

801
sae/input.js Normal file
View file

@ -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]+'<br />'); //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);
}
}

652
sae/memory.js Normal file
View file

@ -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];
}
}*/
}

1376
sae/playfield.js Normal file

File diff suppressed because it is too large Load diff

191
sae/rtc.js Normal file
View file

@ -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);
}
}

86
sae/serial.js Normal file
View file

@ -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 += '<br/>';
else if (buf[i] == 9)
str += '&nbsp;&nbsp;&nbsp;';
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;
}
}

419
sae/utils.js Normal file
View file

@ -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);
}
}
}

245
sae/video.js Normal file
View file

@ -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);
}
}
}