update 0.7.0
This commit is contained in:
parent
032b58df5d
commit
0c5b018866
24 changed files with 21049 additions and 0 deletions
459
sae/amiga.js
Normal file
459
sae/amiga.js
Normal 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:'' };
|
||||
}
|
||||
Loading…
Reference in a new issue