2013-11-06 01:12:55 +01:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @constructor
|
2015-02-25 18:21:54 +01:00
|
|
|
* @param {CPU} dev
|
2013-11-06 01:12:55 +01:00
|
|
|
*/
|
|
|
|
function DMA(dev)
|
|
|
|
{
|
2015-09-15 21:58:40 +02:00
|
|
|
/** @const @type {Memory} */
|
2014-04-14 16:41:16 +02:00
|
|
|
this.memory = dev.memory;
|
|
|
|
|
2015-05-17 03:06:54 +02:00
|
|
|
this.channel_addr = new Int32Array(4);
|
|
|
|
this.channel_count = new Int32Array(4);
|
2014-04-14 16:41:16 +02:00
|
|
|
|
|
|
|
this.lsb_msb_flipflop = 0;
|
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
var io = dev.io;
|
|
|
|
io.register_write(0x04, this, this.port_write.bind(this, 0x04));
|
|
|
|
io.register_write(0x05, this, this.port_write.bind(this, 0x05));
|
|
|
|
io.register_write(0x0A, this, this.portA_write);
|
|
|
|
io.register_write(0x0B, this, this.portB_write);
|
|
|
|
io.register_write(0x0C, this, this.portC_write);
|
|
|
|
io.register_write(0x81, this, this.port81_write);
|
2015-05-18 22:18:59 +02:00
|
|
|
}
|
2014-09-29 07:10:47 +02:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
DMA.prototype.get_state = function()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
this.channel_addr,
|
|
|
|
this.channel_count,
|
|
|
|
this.lsb_msb_flipflop,
|
2015-01-12 18:05:10 +01:00
|
|
|
];
|
2014-04-14 16:41:16 +02:00
|
|
|
};
|
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
DMA.prototype.set_state = function(state)
|
|
|
|
{
|
|
|
|
this.channel_addr = state[0];
|
|
|
|
this.channel_count = state[1];
|
|
|
|
this.lsb_msb_flipflop = state[2];
|
|
|
|
};
|
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
DMA.prototype.port_write = function(port, data_byte)
|
|
|
|
{
|
|
|
|
dbg_log("port " + port + " write " + data_byte, LOG_DMA);
|
|
|
|
|
|
|
|
if(port < 8)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
var channel = port >> 1;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
if(port & 1)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
this.channel_count[channel] = this.flipflop_get(this.channel_count[channel], data_byte);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-04-14 16:41:16 +02:00
|
|
|
else
|
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
this.channel_addr[channel] = this.flipflop_get(this.channel_addr[channel], data_byte);
|
2014-04-14 16:41:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
DMA.prototype.port_read = function(port)
|
|
|
|
{
|
|
|
|
if(port < 8)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
var channel = port >> 1;
|
|
|
|
|
|
|
|
if(port & 1)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
return this.channel_count[channel];
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
// Bug?
|
2015-05-17 03:06:54 +02:00
|
|
|
return this.channel_addr[channel];
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-04-14 16:41:16 +02:00
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
dbg_log("port " + h(port) + " read", LOG_DMA);
|
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
DMA.prototype.portA_write = function(data_byte)
|
|
|
|
{
|
|
|
|
dbg_log("port A write: " + h(data_byte), LOG_DMA);
|
|
|
|
};
|
|
|
|
|
|
|
|
DMA.prototype.portB_write = function(data_byte)
|
|
|
|
{
|
|
|
|
dbg_log("port B write: " + h(data_byte), LOG_DMA);
|
|
|
|
};
|
|
|
|
|
|
|
|
DMA.prototype.portC_write = function(data_byte)
|
|
|
|
{
|
|
|
|
this.lsb_msb_flipflop = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DMA.prototype.port81_write = function(data_byte)
|
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
this.channel_addr[2] = this.channel_addr[2] & 0xFFFF | data_byte << 16;
|
2014-04-14 16:41:16 +02:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
// read data, write to memory
|
|
|
|
DMA.prototype.do_read = function(buffer, start, len, channel, fn)
|
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
var read_count = this.channel_count[channel] + 1,
|
|
|
|
addr = this.channel_addr[channel];
|
2014-04-14 16:41:16 +02:00
|
|
|
|
|
|
|
dbg_log("DMA write channel " + channel, LOG_DMA);
|
|
|
|
dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
|
|
|
|
|
|
|
|
if(len < read_count)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
dbg_log("DMA should read more than provided: " + h(len) + " " + h(read_count), LOG_DMA);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
if(start + read_count > buffer.byteLength)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
dbg_log("DMA read outside of buffer", LOG_DMA);
|
|
|
|
fn(true);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-04-14 16:41:16 +02:00
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
var memory = this.memory;
|
2015-05-17 03:06:54 +02:00
|
|
|
this.channel_addr[channel] += read_count;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
buffer.get(start, read_count, function(data)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
memory.write_blob(data, addr);
|
|
|
|
fn(false);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
// write data, read memory
|
|
|
|
DMA.prototype.do_write = function(buffer, start, len, channel, fn)
|
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
var read_count = this.channel_count[channel],
|
|
|
|
addr = this.channel_addr[channel];
|
2013-11-28 00:59:36 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
dbg_log("DMA write channel " + channel, LOG_DMA);
|
|
|
|
dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
if(len < read_count)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
dbg_log("DMA should read more than provided", LOG_DMA);
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
if(start + read_count > buffer.byteLength)
|
|
|
|
{
|
|
|
|
dbg_log("DMA write outside of buffer", LOG_DMA);
|
|
|
|
fn(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-05-17 03:06:54 +02:00
|
|
|
this.channel_addr[channel] += read_count;
|
2014-04-14 16:41:16 +02:00
|
|
|
|
|
|
|
buffer.set(start,
|
2015-11-13 02:01:17 +01:00
|
|
|
this.memory.mem8.subarray(addr, addr + read_count + 1),
|
2014-04-14 16:41:16 +02:00
|
|
|
function() {
|
|
|
|
fn(false);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
DMA.prototype.flipflop_get = function(old_dword, new_byte)
|
|
|
|
{
|
|
|
|
this.lsb_msb_flipflop ^= 1;
|
2013-11-28 00:59:36 +01:00
|
|
|
|
2014-04-14 16:41:16 +02:00
|
|
|
if(this.lsb_msb_flipflop)
|
|
|
|
{
|
|
|
|
// low byte
|
|
|
|
return old_dword & ~0xFF | new_byte;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-04-14 16:41:16 +02:00
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-04-14 16:41:16 +02:00
|
|
|
// high byte
|
|
|
|
return old_dword & ~0xFF00 | new_byte << 8;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
}
|