Fixing some major bugs with interrupts.

This commit is contained in:
Adam Becker 2016-11-25 00:39:20 -07:00
parent 4c911c465d
commit a1038d7fa9
6 changed files with 123 additions and 141 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
bin/
cmake-build-*/
.idea/

View file

@ -48,8 +48,8 @@ void cpu::run(int count) {
state.cop0.regs[13] &= ~(1 << 10);
}
auto iec = state.cop0.regs[12] & 1;
auto irq = state.cop0.regs[12] & state.cop0.regs[13] & 0xf0;
auto iec = (state.cop0.regs[12] & 1) != 0;
auto irq = (state.cop0.regs[12] & state.cop0.regs[13] & 0xff00) != 0;
if (iec && irq) {
enter_exception(0x0);
@ -74,10 +74,10 @@ void cpu::enter_exception(uint32_t code) {
if (state.is_branch_delay_slot) {
epc = state.regs.this_pc - 4;
cause |= (1 << 31);
cause |= 0x80000000;
} else {
epc = state.regs.this_pc;
cause &= ~(1 << 31);
cause &= ~0x80000000;
}
state.cop0.regs[12] = status;
@ -105,7 +105,7 @@ static uint32_t segments[8] = {
0x7fffffff, //
0x1fffffff, // kseg0 ($8000_0000 - $9fff_ffff)
0x1fffffff, // kseg1 ($a000_0000 - $bfff_ffff)
0x3fffffff, // kseg2 ($c000_0000 - $ffff_ffff)
0xffffffff, // kseg2 ($c000_0000 - $ffff_ffff)
0xffffffff //
};
@ -188,15 +188,23 @@ void cpu::write_data_word(uint32_t address, uint32_t data) {
}
uint32_t cpu::mmio_read(int, uint32_t address) {
switch (address - 0x1f801070) {
case 0: return state.i_stat;
case 4: return state.i_mask;
switch (address) {
case 0x1f801070:
return state.i_stat;
case 0x1f801074:
return state.i_mask;
}
}
void cpu::mmio_write(int, uint32_t address, uint32_t data) {
switch (address - 0x1f801070) {
case 0: state.i_stat = state.i_stat & data; break;
case 4: state.i_mask = data & 0x7ff; break;
switch (address) {
case 0x1f801070:
state.i_stat = state.i_stat & data;
break;
case 0x1f801074:
state.i_mask = data & 0x7ff;
break;
}
}

View file

@ -11,13 +11,6 @@ static inline uint32_t get_register_index(uint32_t address) {
return (address >> 2) & 3;
}
void dma::initialize() {
state.dpcr = 0x07654321;
state.dicr = 0x00000000;
state.channels[2].dst_address = 0x1f801810;
}
uint32_t dma::mmio_read(int size, uint32_t address) {
auto channel = get_channel_index(address);
if (channel == 7) {
@ -30,9 +23,9 @@ uint32_t dma::mmio_read(int size, uint32_t address) {
}
else {
switch ((address >> 2) & 3) {
case 0: return state.channels[channel].base_address;
case 1: return state.channels[channel].block_control;
case 2: return state.channels[channel].channel_control;
case 0: return state.channels[channel].address;
case 1: return state.channels[channel].counter;
case 2: return state.channels[channel].control;
}
}
@ -46,9 +39,9 @@ void dma::mmio_write(int size, uint32_t address, uint32_t data) {
case 0: state.dpcr = data & 0xffffffff; break;
case 1:
state.dicr &= (0xff000000 );
state.dicr |= (0x00ff803f & data);
state.dicr &= ~(0x7f000000 & data);
state.dicr &= ( 0xff000000);
state.dicr |= (data & 0x00ff803f);
state.dicr &= ~(data & 0x7f000000);
break;
case 2: break;
@ -57,139 +50,100 @@ void dma::mmio_write(int size, uint32_t address, uint32_t data) {
}
else {
switch ((address >> 2) & 3) {
case 0: state.channels[channel].base_address = data; break;
case 1: state.channels[channel].block_control = data; break;
case 2: state.channels[channel].channel_control = data; break;
case 0: state.channels[channel].address = data & 0x00ffffff; break;
case 1: state.channels[channel].counter = data & 0xffffffff; break;
case 2: state.channels[channel].control = data & 0x71770703; break;
}
}
dma::main();
}
static void dma_sync_mode_0(dma::channel_t &channel) {
if ((channel.channel_control & 0x10000000) == 0) {
return;
}
auto count = channel.block_control & 0xffff;
auto address = channel.base_address & 0xffffff;
auto address_step = (channel.channel_control & 2) ? 0xfffffffc : 0x00000004;
do {
if (count == 1) {
bus::write_word(address, 0x00ffffff);
} else {
bus::write_word(address, address - 4);
}
address += address_step;
count = (count - 1) & 0xffff;
if (count == 0) {
break;
}
}
while (true);
channel.channel_control &= ~0x11000000;
void dma::main() {
if (state.dpcr & 0x08000000) { run_channel(6); }
if (state.dpcr & 0x00800000) { run_channel(5); }
if (state.dpcr & 0x00080000) { run_channel(4); }
if (state.dpcr & 0x00008000) { run_channel(3); }
if (state.dpcr & 0x00000800) { run_channel(2); }
if (state.dpcr & 0x00000080) { run_channel(1); }
if (state.dpcr & 0x00000008) { run_channel(0); }
}
static void dma_sync_mode_1(dma::channel_t &channel) {
if ((channel.channel_control & 0x01000000) == 0) {
return;
}
auto bs = channel.block_control & 0xffff;
auto ba = channel.block_control >> 16;
static void run_channel_2_data_read() {
auto address = state.channels[2].address & 0x00ffffff;
auto bs = (state.channels[2].counter >> 0) & 0xffff;
auto ba = (state.channels[2].counter >> 16) & 0xffff;
bs = bs ? bs : 0x10000;
ba = ba ? ba : 0x10000;
auto length = bs * ba;
auto address = channel.base_address & 0x00ffffff;
auto address_step = (channel.channel_control & 2) ? 0xfffffffc : 0x00000004;
for (int i = 0; i < length; i++) {
auto data = bus::read_word(address);
bus::write_word(channel.dst_address, data);
address += address_step;
for (int a = 0; a < ba; a++) {
for (int s = 0; s < bs; s++) {
auto data = bus::read_word(0x1f801810);
bus::write_word(address, data);
address += 4;
}
}
channel.channel_control &= ~0x11000000;
state.channels[2].control &= ~0x01000000;
}
static void dma_sync_mode_2(dma::channel_t &channel) {
if ((channel.channel_control & 0x01000000) == 0) {
return;
static void run_channel_2_data_write() {
auto address = state.channels[2].address & 0x00ffffff;
auto bs = (state.channels[2].counter >> 0) & 0xffff;
auto ba = (state.channels[2].counter >> 16) & 0xffff;
bs = bs ? bs : 0x10000;
ba = ba ? ba : 0x10000;
for (int a = 0; a < ba; a++) {
for (int s = 0; s < bs; s++) {
auto data = bus::read_word(address);
bus::write_word(0x1f801810, data);
address += 4;
}
}
auto address = channel.base_address & 0xffffff;
state.channels[2].control &= ~0x01000000;
}
while (true) {
auto header = bus::read_word(address);
auto count = header >> 24;
static void run_channel_2_list() {
auto address = state.channels[2].address & 0xffffff;
while (address != 0xffffff) {
auto value = bus::read_word(address);
auto count = value >> 24;
for (auto index = 0; index < count; index++) {
address += 4;
auto command = bus::read_word(address);
bus::write_word(channel.dst_address, command);
auto command = bus::read_word(address += 4);
bus::write_word(0x1f801810, command);
}
address = header & 0xffffff;
if (address == 0xffffff) {
break;
}
address = value & 0xffffff;
}
channel.channel_control &= ~0x11000000;
state.channels[2].control &= ~0x01000000;
}
static void dma_irq(int level) {
if (!(state.dicr & 0x80000000) && level) {
bus::irq_req(3);
static void run_channel_6() {
auto address = state.channels[6].address & 0xffffff;
auto counter = state.channels[6].counter & 0xffff;
counter = counter ? counter : 0x10000;
for (int i = 1; i < counter; i++) {
bus::write_word(address, address - 4);
address -= 4;
}
state.dicr &= ~0x80000000;
state.dicr |= level << 31;
bus::write_word(address, 0x00ffffff);
state.channels[6].control &= ~0x11000000;
}
void dma::main() {
auto channel_irq_enable = 1 << 16;
auto channel_irq_request = 1 << 24;
auto mask = 0x00000008;
for (int i = 0; i < 7; i++) {
auto &channel = state.channels[i];
if (state.dpcr & mask) {
switch ((channel.channel_control >> 9) & 3) {
case 0: dma_sync_mode_0(channel); break;
case 1: dma_sync_mode_1(channel); break;
case 2: dma_sync_mode_2(channel); break;
case 3: break;
}
if (state.dicr & channel_irq_enable) {
state.dicr |= channel_irq_request;
}
}
channel_irq_enable <<= 1;
channel_irq_request <<= 1;
mask <<= 4;
}
int force = (state.dicr >> 15) & 1;
int enable = (state.dicr >> 16) & 0x7f;
int master = (state.dicr >> 23) & 1;
int request = (state.dicr >> 24) & 0x7f;
if (force || (master && (enable & request) != 0)) {
dma_irq(1);
} else {
dma_irq(0);
}
void dma::run_channel(int n) {
if (n == 2 && state.channels[2].control == 0x01000200) { return run_channel_2_data_read(); }
if (n == 2 && state.channels[2].control == 0x01000201) { return run_channel_2_data_write(); }
if (n == 2 && state.channels[2].control == 0x01000401) { return run_channel_2_list(); }
if (n == 6 && state.channels[6].control == 0x11000002) { return run_channel_6(); }
}

View file

@ -4,27 +4,24 @@
#include <cstdint>
namespace dma {
struct channel_t {
uint32_t dst_address;
uint32_t base_address;
uint32_t block_control;
uint32_t channel_control;
};
struct state_t {
uint32_t dpcr = 0x07654321;
uint32_t dicr;
uint32_t dicr = 0x00000000;
channel_t channels[7];
struct {
uint32_t address;
uint32_t counter;
uint32_t control;
} channels[7];
};
void initialize();
uint32_t mmio_read(int size, uint32_t address);
void mmio_write(int size, uint32_t address, uint32_t data);
void main();
void run_channel(int n);
}
#endif //PSXACT_DMA_CORE_HPP

View file

@ -6,7 +6,7 @@
static gpu::state_t state;
static int gp0_command_size[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $00
1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // $10
1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 1, 1, 9, 1, 1, 1, // $20
6, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, // $30
@ -149,6 +149,27 @@ static inline void write_gp0(uint32_t data) {
case 0x00: get_gp0_data(); break; // nop
case 0x01: get_gp0_data(); break; // clear texture cache
case 0x02: { // fill rectangle
auto color = gp0_to_color(get_gp0_data());
auto point1 = gp0_to_point(get_gp0_data());
auto point2 = gp0_to_point(get_gp0_data());
point1.x = (point1.x + 0x0) & ~0xf;
point2.x = (point2.x + 0xf) & ~0xf;
for (int y = 0; y < point2.y; y++) {
for (int x = 0; x < point2.x; x++) {
gpu::draw_point(point1.x + x,
point1.y + y,
color.r,
color.g,
color.b);
}
}
break;
}
case 0x28: { // monochrome quad, opaque
auto color = get_gp0_data();
auto point1 = get_gp0_data();

View file

@ -9,11 +9,12 @@ int main(int argc, char *argv[]) {
return -1;
}
freopen("C:/Users/Adam.Becker/stdout.log", "w", stdout);
std::string bios_file_name(argv[1]);
std::string game_file_name(argv[2]);
cpu::initialize();
dma::initialize();
bus::initialize(bios_file_name, game_file_name);
renderer::initialize();