v86/src/dma.js
Awal Garg 8222d2e6e0 Squash
restore memcpy comment
delete all the things!
fix jshint issues
restore memcpy comment
remove duplicate fxsave assignment
Count cache drops
Use already available physical address instead of calling read_imm8
Remove useless assertion
Just move around to reduce later diff
Run jit paging test with assertions enabled
Run jit-paging test on CI
Extend jit-paging test
Fix deleting invalidated code across memory pages
Add jit-paging test to gitlab ci
Remove jit_in_progress
Clean up old comments, use bool for jit_jump
Fix state image not begin garbage collected
Add ENABLE_PROFILER_TIMES to configure slow profiling times
Move to jit_generate and jit_run_interpreted to separate function
Add missing struct field
Fix: Don't write jit cache entry until no more faults can happen
Download image for jit paging test
Add missing initialiser
Mark jit_{generate,run_interpreted} as static
Specify full path to profiler.h
Clean up duplicate/missing declaration after rebase
mmap error handling, line length and fix some warnings
remove further unused code
move js imports to single header file
2020-07-21 20:10:13 -05:00

425 lines
14 KiB
JavaScript

"use strict";
/**
* @constructor
* @param {CPU} cpu
*/
function DMA(cpu)
{
/** @const @type {CPU} */
this.cpu = cpu;
this.channel_page = new Uint8Array(8);
this.channel_pagehi = new Uint8Array(8);
this.channel_addr = new Uint16Array(8);
this.channel_addr_init = new Uint16Array(8);
this.channel_count = new Uint16Array(8);
this.channel_count_init = new Uint16Array(8);
this.channel_mask = new Uint8Array(8);
this.channel_mode = new Uint8Array(8);
this.unmask_listeners = [];
this.lsb_msb_flipflop = 0;
var io = cpu.io;
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_read(0x00, this, this.port_addr_read.bind(this, 0));
io.register_read(0x02, this, this.port_addr_read.bind(this, 1));
io.register_read(0x04, this, this.port_addr_read.bind(this, 2));
io.register_read(0x06, this, this.port_addr_read.bind(this, 3));
io.register_read(0x01, this, this.port_count_read.bind(this, 0));
io.register_read(0x03, this, this.port_count_read.bind(this, 1));
io.register_read(0x05, this, this.port_count_read.bind(this, 2));
io.register_read(0x07, this, this.port_count_read.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_read(0xC0, this, this.port_addr_read.bind(this, 4));
io.register_read(0xC4, this, this.port_addr_read.bind(this, 5));
io.register_read(0xC8, this, this.port_addr_read.bind(this, 6));
io.register_read(0xCC, this, this.port_addr_read.bind(this, 7));
io.register_read(0xC2, this, this.port_count_read.bind(this, 4));
io.register_read(0xC6, this, this.port_count_read.bind(this, 5));
io.register_read(0xCA, this, this.port_count_read.bind(this, 6));
io.register_read(0xCE, this, this.port_count_read.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_read(0x87, this, this.port_page_read.bind(this, 0));
io.register_read(0x83, this, this.port_page_read.bind(this, 1));
io.register_read(0x81, this, this.port_page_read.bind(this, 2));
io.register_read(0x82, this, this.port_page_read.bind(this, 3));
io.register_read(0x8F, this, this.port_page_read.bind(this, 4));
io.register_read(0x8B, this, this.port_page_read.bind(this, 5));
io.register_read(0x89, this, this.port_page_read.bind(this, 6));
io.register_read(0x8A, this, this.port_page_read.bind(this, 7));
io.register_write(0x487, this, this.port_pagehi_write.bind(this, 0));
io.register_write(0x483, this, this.port_pagehi_write.bind(this, 1));
io.register_write(0x481, this, this.port_pagehi_write.bind(this, 2));
io.register_write(0x482, this, this.port_pagehi_write.bind(this, 3));
io.register_write(0x48B, this, this.port_pagehi_write.bind(this, 5));
io.register_write(0x489, this, this.port_pagehi_write.bind(this, 6));
io.register_write(0x48A, this, this.port_pagehi_write.bind(this, 7));
io.register_read(0x487, this, this.port_pagehi_read.bind(this, 0));
io.register_read(0x483, this, this.port_pagehi_read.bind(this, 1));
io.register_read(0x481, this, this.port_pagehi_read.bind(this, 2));
io.register_read(0x482, this, this.port_pagehi_read.bind(this, 3));
io.register_read(0x48B, this, this.port_pagehi_read.bind(this, 5));
io.register_read(0x489, this, this.port_pagehi_read.bind(this, 6));
io.register_read(0x48A, this, this.port_pagehi_read.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));
io.register_read(0x0F, this, this.port_multimask_read.bind(this, 0));
io.register_read(0xDE, this, this.port_multimask_read.bind(this, 4));
io.register_write(0x0B, this, this.port_mode_write.bind(this, 0));
io.register_write(0xD6, this, this.port_mode_write.bind(this, 4));
io.register_write(0x0C, this, this.portC_write);
io.register_write(0xD8, this, this.portC_write);
}
DMA.prototype.get_state = function()
{
return [
this.channel_page,
this.channel_pagehi,
this.channel_addr,
this.channel_addr_init,
this.channel_count,
this.channel_count_init,
this.channel_mask,
this.channel_mode,
this.lsb_msb_flipflop,
];
};
DMA.prototype.set_state = function(state)
{
this.channel_page = state[0];
this.channel_pagehi = state[1];
this.channel_addr = state[2];
this.channel_addr_init = state[3];
this.channel_count = state[4];
this.channel_count_init = state[5];
this.channel_mask = state[6];
this.channel_mode = state[7];
this.lsb_msb_flipflop = state[8];
};
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, false);
this.channel_count_init[channel] =
this.flipflop_get(this.channel_count_init[channel], data_byte, true);
};
DMA.prototype.port_count_read = function(channel)
{
dbg_log("count read [" + channel + "] -> " + h(this.channel_count[channel]), LOG_DMA);
return this.flipflop_read(this.channel_count[channel]);
};
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, false);
this.channel_addr_init[channel] =
this.flipflop_get(this.channel_addr_init[channel], data_byte, true);
};
DMA.prototype.port_addr_read = function(channel)
{
dbg_log("addr read [" + channel + "] -> " + h(this.channel_addr[channel]), LOG_DMA);
return this.flipflop_read(this.channel_addr[channel]);
};
DMA.prototype.port_pagehi_write = function(channel, data_byte)
{
dbg_log("pagehi write [" + channel + "] = " + h(data_byte), LOG_DMA);
this.channel_pagehi[channel] = data_byte;
};
DMA.prototype.port_pagehi_read = function(channel)
{
dbg_log("pagehi read [" + channel + "]", LOG_DMA);
return this.channel_pagehi[channel];
};
DMA.prototype.port_page_write = function(channel, data_byte)
{
dbg_log("page write [" + channel + "] = " + h(data_byte), LOG_DMA);
this.channel_page[channel] = data_byte;
};
DMA.prototype.port_page_read = function(channel)
{
dbg_log("page read [" + channel + "]", LOG_DMA);
return this.channel_page[channel];
};
DMA.prototype.port_singlemask_write = function(channel_offset, data_byte)
{
var channel = (data_byte & 0x3) + channel_offset;
var value = data_byte & 0x4 ? 1 : 0;
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];
value |= this.channel_mask[channel_offset + 1] << 1;
value |= this.channel_mask[channel_offset + 2] << 2;
value |= this.channel_mask[channel_offset + 3] << 3;
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;
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(fn, this_value)
{
this.unmask_listeners.push({
fn: fn,
this_value: this_value,
});
};
DMA.prototype.update_mask = function(channel, value)
{
if(this.channel_mask[channel] !== value)
{
this.channel_mask[channel] = value;
if(!value)
{
dbg_log("firing on_unmask(" + channel + ")", LOG_DMA);
for(var i = 0; i < this.unmask_listeners.length; i++)
{
this.unmask_listeners[i].fn.call(
this.unmask_listeners[i].this_value,
channel
);
}
}
}
};
// read data, write to memory
DMA.prototype.do_read = function(buffer, start, len, channel, fn)
{
var read_count = this.count_get_8bit(channel),
addr = this.address_get_8bit(channel);
dbg_log("DMA write channel " + channel, LOG_DMA);
dbg_log("to " + h(addr) + " len " + h(read_count), LOG_DMA);
if(len < read_count)
{
dbg_log("DMA should read more than provided: " + h(len) + " " + h(read_count), LOG_DMA);
}
if(start + read_count > buffer.byteLength)
{
dbg_log("DMA read outside of buffer", LOG_DMA);
fn(true);
}
else
{
var cpu = this.cpu;
this.channel_addr[channel] += read_count;
buffer.get(start, read_count, function(data)
{
cpu.write_blob(data, addr);
fn(false);
});
}
};
// write data, read memory
// start and len in bytes
DMA.prototype.do_write = function(buffer, start, len, channel, fn)
{
var read_count = (this.channel_count[channel] + 1) & 0xFFFF,
bytes_per_count = channel >= 5 ? 2 : 1,
read_bytes = read_count * bytes_per_count,
addr = this.address_get_8bit(channel),
unfinished = false,
want_more = false,
autoinit = this.channel_mode[channel] & 0x10;
dbg_log("DMA write channel " + channel, LOG_DMA);
dbg_log("to " + h(addr) + " len " + h(read_bytes), LOG_DMA);
if(len < read_bytes)
{
dbg_log("DMA should read more than provided", LOG_DMA);
read_count = Math.floor(len / bytes_per_count);
read_bytes = read_count * bytes_per_count;
unfinished = true;
}
else if(len > read_bytes)
{
dbg_log("DMA attempted to read more than provided", LOG_DMA);
want_more = true;
}
if(start + read_bytes > buffer.byteLength)
{
dbg_log("DMA write outside of buffer", LOG_DMA);
fn(true);
}
else
{
this.channel_addr[channel] += read_count;
this.channel_count[channel] -= read_count;
// when complete, counter should underflow to 0xFFFF
if(!unfinished && autoinit)
{
dbg_log("DMA autoinit", LOG_DMA);
this.channel_addr[channel] = this.channel_addr_init[channel];
this.channel_count[channel] = this.channel_count_init[channel];
}
buffer.set(start,
this.cpu.mem8.subarray(addr, addr + read_bytes),
() =>
{
if(want_more && autoinit)
{
dbg_log("DMA continuing from start", LOG_DMA);
this.do_write(buffer, start + read_bytes, len - read_bytes, channel, fn);
}
else
{
fn(false);
}
}
);
}
};
DMA.prototype.address_get_8bit = function(channel)
{
var addr = this.channel_addr[channel];
// http://wiki.osdev.org/ISA_DMA#16_bit_issues
if(channel >= 5)
{
addr = (addr << 1);
}
addr &= 0xFFFF;
addr |= this.channel_page[channel] << 16;
addr |= this.channel_pagehi[channel] << 24;
return addr;
};
DMA.prototype.count_get_8bit = function(channel)
{
var count = this.channel_count[channel] + 1;
if(channel >= 5)
{
count *= 2;
}
return count;
};
DMA.prototype.flipflop_get = function(old_dword, new_byte, continuing)
{
if(!continuing)
{
this.lsb_msb_flipflop ^= 1;
}
if(this.lsb_msb_flipflop)
{
// low byte
return old_dword & ~0xFF | new_byte;
}
else
{
// high byte
return old_dword & ~0xFF00 | new_byte << 8;
}
};
DMA.prototype.flipflop_read = function(dword)
{
this.lsb_msb_flipflop ^= 1;
if(this.lsb_msb_flipflop)
{
// low byte
return dword & 0xFF;
}
else
{
// high byte
return (dword >> 8) & 0xFF;
}
};