v86/src/dma.js

303 lines
8.5 KiB
JavaScript
Raw Normal View History

2013-11-06 01:12:55 +01:00
"use strict";
/**
* @constructor
2016-08-02 04:15:24 +02:00
* @param {CPU} cpu
2013-11-06 01:12:55 +01:00
*/
2016-08-02 04:15:24 +02:00
function DMA(cpu)
2013-11-06 01:12:55 +01:00
{
2016-08-02 04:15:24 +02:00
/** @const @type {CPU} */
this.cpu = cpu;
2014-04-14 16:41:16 +02:00
2017-11-17 08:13:00 +01:00
this.channel_addr = new Int32Array(8);
this.channel_count = new Int32Array(8);
this.channel_mask = new Uint8Array(8);
this.channel_mode = new Uint8Array(8);
this.channel_on_unmask = [];
2014-04-14 16:41:16 +02:00
this.lsb_msb_flipflop = 0;
2016-08-02 04:15:24 +02:00
var io = cpu.io;
2017-11-17 08:13:00 +01:00
io.register_write(0x00, this, this.port_addr_write.bind(this, 0));
io.register_write(0x02, this, this.port_addr_write.bind(this, 1));
io.register_write(0x04, this, this.port_addr_write.bind(this, 2));
io.register_write(0x06, this, this.port_addr_write.bind(this, 3));
io.register_write(0x01, this, this.port_count_write.bind(this, 0));
io.register_write(0x03, this, this.port_count_write.bind(this, 1));
io.register_write(0x05, this, this.port_count_write.bind(this, 2));
io.register_write(0x07, this, this.port_count_write.bind(this, 3));
io.register_write(0xC0, this, this.port_addr_write.bind(this, 4));
io.register_write(0xC4, this, this.port_addr_write.bind(this, 5));
io.register_write(0xC8, this, this.port_addr_write.bind(this, 6));
io.register_write(0xCC, this, this.port_addr_write.bind(this, 7));
io.register_write(0xC2, this, this.port_count_write.bind(this, 4));
io.register_write(0xC6, this, this.port_count_write.bind(this, 5));
io.register_write(0xCA, this, this.port_count_write.bind(this, 6));
io.register_write(0xCE, this, this.port_count_write.bind(this, 7));
io.register_write(0x87, this, this.port_page_write.bind(this, 0));
io.register_write(0x83, this, this.port_page_write.bind(this, 1));
io.register_write(0x81, this, this.port_page_write.bind(this, 2));
io.register_write(0x82, this, this.port_page_write.bind(this, 3));
io.register_write(0x8F, this, this.port_page_write.bind(this, 4));
io.register_write(0x8B, this, this.port_page_write.bind(this, 5));
io.register_write(0x89, this, this.port_page_write.bind(this, 6));
io.register_write(0x8A, this, this.port_page_write.bind(this, 7));
io.register_write(0x0A, this, this.port_singlemask_write.bind(this, 0));
io.register_write(0xD4, this, this.port_singlemask_write.bind(this, 4));
io.register_write(0x0F, this, this.port_multimask_write.bind(this, 0));
io.register_write(0xDE, this, this.port_multimask_write.bind(this, 4));
2017-11-17 08:13:00 +01:00
io.register_write(0x0B, this, this.port_mode_write.bind(this, 0));
io.register_write(0xD6, this, this.port_mode_write.bind(this, 4));
2017-11-17 08:13:00 +01:00
io.register_write(0x0C, this, this.portC_write);
2017-11-17 08:13:00 +01:00
io.register_write(0xD8, this, this.portC_write);
}
DMA.prototype.get_state = function()
{
return [
this.channel_addr,
this.channel_count,
this.lsb_msb_flipflop,
];
2014-04-14 16:41:16 +02:00
};
DMA.prototype.set_state = function(state)
{
this.channel_addr = state[0];
this.channel_count = state[1];
this.lsb_msb_flipflop = state[2];
};
2017-11-17 08:13:00 +01:00
DMA.prototype.port_count_write = function(channel, data_byte)
{
dbg_log("count write [" + channel + "] = " + h(data_byte), LOG_DMA);
this.channel_count[channel] = this.flipflop_get(this.channel_count[channel], data_byte);
}
DMA.prototype.port_addr_write = function(channel, data_byte)
{
dbg_log("addr write [" + channel + "] = " + h(data_byte), LOG_DMA);
this.channel_addr[channel] = this.flipflop_get(this.channel_addr[channel], data_byte);
}
DMA.prototype.port_page_write = function(channel, data_byte)
{
dbg_log("page write [" + channel + "] = " + h(data_byte), LOG_DMA);
this.channel_addr[channel] = this.channel_addr[channel] & 0xFFFF | data_byte << 16;
}
DMA.prototype.port_page_read = function(channel)
{
dbg_log("page read [" + channel + "]", LOG_DMA);
return this.channel_addr[channel] >> 16;
}
DMA.prototype.port_singlemask_write = function(channel_offset, data_byte)
{
var channel = data_byte & 0x3 + channel_offset;
2017-11-17 08:13:00 +01:00
var value = !!(data_byte & 0x8);
dbg_log("singlechannel mask write [" + channel + "] = " + value, LOG_DMA);
this.update_mask(channel, value);
}
DMA.prototype.port_multimask_write = function(channel_offset, data_byte)
{
dbg_log("multichannel mask write: " + h(data_byte), LOG_DMA);
for(var i = 0; i < 4; i++)
{
this.update_mask(channel_offset + i, data_byte & (1 << i));
}
}
DMA.prototype.port_multimask_read = function(channel_offset)
{
var value = 0;
value |= this.channel_mask[channel_offset + 0] * 0x1;
value |= this.channel_mask[channel_offset + 1] * 0x2;
value |= this.channel_mask[channel_offset + 2] * 0x4;
value |= this.channel_mask[channel_offset + 3] * 0x8;
dbg_log("multichannel mask read: " + h(value), LOG_DMA);
return value;
}
DMA.prototype.port_mode_write = function(channel_offset, data_byte)
{
var channel = data_byte & 0x3 + channel_offset;
2017-11-17 08:13:00 +01:00
dbg_log("mode write [" + channel + "] = " + h(data_byte), LOG_DMA);
this.channel_mode[channel] = data_byte;
}
DMA.prototype.portC_write = function(data_byte)
{
dbg_log("flipflop reset", LOG_DMA);
this.lsb_msb_flipflop = 0;
}
DMA.prototype.on_unmask = function(channel, fn)
{
this.channel_on_unmask[channel] = fn;
}
DMA.prototype.update_mask = function(channel, value)
{
if(this.channel_mask[channel] !== value)
{
this.channel_mask[channel] ^= 1;
if(!value && this.channel_on_unmask[channel])
{
dbg_log("firing on_unmask[" + channel + "]", LOG_DMA);
this.channel_on_unmask[channel]();
}
}
}
/*
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
2017-11-17 08:13:00 +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
}
2017-11-17 08:13:00 +01: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
{
2016-08-02 04:15:24 +02:00
var cpu = this.cpu;
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
{
2016-08-02 04:15:24 +02:00
cpu.write_blob(data, addr);
2014-04-14 16:41:16 +02:00
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,
2016-08-02 04:15:24 +02:00
this.cpu.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
}
}