emulator/gba3/js/savedata.js
2019-06-14 09:33:19 +08:00

308 lines
7.4 KiB
JavaScript

function SRAMSavedata(size) {
MemoryView.call(this, new ArrayBuffer(size), 0);
this.writePending = false;
};
SRAMSavedata.prototype = Object.create(MemoryView.prototype);
SRAMSavedata.prototype.store8 = function(offset, value) {
this.view.setInt8(offset, value);
this.writePending = true;
};
SRAMSavedata.prototype.store16 = function(offset, value) {
this.view.setInt16(offset, value, true);
this.writePending = true;
};
SRAMSavedata.prototype.store32 = function(offset, value) {
this.view.setInt32(offset, value, true);
this.writePending = true;
};
function FlashSavedata(size) {
MemoryView.call(this, new ArrayBuffer(size), 0);
this.COMMAND_WIPE = 0x10;
this.COMMAND_ERASE_SECTOR = 0x30;
this.COMMAND_ERASE = 0x80;
this.COMMAND_ID = 0x90;
this.COMMAND_WRITE = 0xA0;
this.COMMAND_SWITCH_BANK = 0xB0;
this.COMMAND_TERMINATE_ID = 0xF0;
this.ID_PANASONIC = 0x1B32;
this.ID_SANYO = 0x1362;
this.bank0 = new DataView(this.buffer, 0, 0x00010000);
if (size > 0x00010000) {
this.id = this.ID_SANYO;
this.bank1 = new DataView(this.buffer, 0x00010000);
} else {
this.id = this.ID_PANASONIC;
this.bank1 = null;
}
this.bank = this.bank0;
this.idMode = false;
this.writePending = false;
this.first = 0;
this.second = 0;
this.command = 0;
this.pendingCommand = 0;
};
FlashSavedata.prototype = Object.create(MemoryView.prototype);
FlashSavedata.prototype.load8 = function(offset) {
if (this.idMode && offset < 2) {
return (this.id >> (offset << 3)) & 0xFF;
} else if (offset < 0x10000) {
return this.bank.getInt8(offset);
} else {
return 0;
}
};
FlashSavedata.prototype.load16 = function(offset) {
return (this.load8(offset) & 0xFF) | (this.load8(offset + 1) << 8);
};
FlashSavedata.prototype.load32 = function(offset) {
return (this.load8(offset) & 0xFF) | (this.load8(offset + 1) << 8) | (this.load8(offset + 2) << 16) | (this.load8(offset + 3) << 24);
};
FlashSavedata.prototype.loadU8 = function(offset) {
return this.load8(offset) & 0xFF;
};
FlashSavedata.prototype.loadU16 = function(offset) {
return (this.loadU8(offset) & 0xFF) | (this.loadU8(offset + 1) << 8);
};
FlashSavedata.prototype.store8 = function(offset, value) {
switch (this.command) {
case 0:
if (offset == 0x5555) {
if (this.second == 0x55) {
switch (value) {
case this.COMMAND_ERASE:
this.pendingCommand = value;
break;
case this.COMMAND_ID:
this.idMode = true;
break;
case this.COMMAND_TERMINATE_ID:
this.idMode = false;
break;
default:
this.command = value;
break;
}
this.second = 0;
this.first = 0;
} else {
this.command = 0;
this.first = value;
this.idMode = false;
}
} else if (offset == 0x2AAA && this.first == 0xAA) {
this.first = 0;
if (this.pendingCommand) {
this.command = this.pendingCommand;
} else {
this.second = value;
}
}
break;
case this.COMMAND_ERASE:
switch (value) {
case this.COMMAND_WIPE:
if (offset == 0x5555) {
for (var i = 0; i < this.view.byteLength; i += 4) {
this.view.setInt32(i, -1);
}
}
break;
case this.COMMAND_ERASE_SECTOR:
if ((offset & 0x0FFF) == 0) {
for (var i = offset; i < offset + 0x1000; i += 4) {
this.bank.setInt32(i, -1);
}
}
break;
}
this.pendingCommand = 0;
this.command = 0;
break;
case this.COMMAND_WRITE:
this.bank.setInt8(offset, value);
this.command = 0;
this.writePending = true;
break;
case this.COMMAND_SWITCH_BANK:
if (this.bank1 && offset == 0) {
if (value == 1) {
this.bank = this.bank1;
} else {
this.bank = this.bank0;
}
}
this.command = 0;
break;
}
};
FlashSavedata.prototype.store16 = function(offset, value) {
throw new Error("Unaligned save to flash!");
};
FlashSavedata.prototype.store32 = function(offset, value) {
throw new Error("Unaligned save to flash!");
};
FlashSavedata.prototype.replaceData = function(memory) {
var bank = this.view === this.bank1;
MemoryView.prototype.replaceData.call(this, memory, 0);
this.bank0 = new DataView(this.buffer, 0, 0x00010000);
if (memory.byteLength > 0x00010000) {
this.bank1 = new DataView(this.buffer, 0x00010000);
} else {
this.bank1 = null;
}
this.bank = bank ? this.bank1 : this.bank0;
};
function EEPROMSavedata(size, mmu) {
MemoryView.call(this, new ArrayBuffer(size), 0);
this.writeAddress = 0;
this.readBitsRemaining = 0;
this.readAddress = 0;
this.command = 0;
this.commandBitsRemaining = 0;
this.realSize = 0;
this.addressBits = 0;
this.writePending = false;
this.dma = mmu.core.irq.dma[3];
this.COMMAND_NULL = 0;
this.COMMAND_PENDING = 1;
this.COMMAND_WRITE = 2;
this.COMMAND_READ_PENDING = 3;
this.COMMAND_READ = 4;
};
EEPROMSavedata.prototype = Object.create(MemoryView.prototype);
EEPROMSavedata.prototype.load8 = function(offset) {
throw new Error("Unsupported 8-bit access!");
};
EEPROMSavedata.prototype.load16 = function(offset) {
return this.loadU16(offset);
};
EEPROMSavedata.prototype.loadU8 = function(offset) {
throw new Error("Unsupported 8-bit access!");
};
EEPROMSavedata.prototype.loadU16 = function(offset) {
if (this.command != this.COMMAND_READ || !this.dma.enable) {
return 1;
}
--this.readBitsRemaining;
if (this.readBitsRemaining < 64) {
var step = 63 - this.readBitsRemaining;
var data = this.view.getUint8((this.readAddress + step) >> 3, false) >> (0x7 - (step & 0x7));
if (!this.readBitsRemaining) {
this.command = this.COMMAND_NULL;
}
return data & 0x1;
}
return 0;
};
EEPROMSavedata.prototype.load32 = function(offset) {
throw new Error("Unsupported 32-bit access!");
};
EEPROMSavedata.prototype.store8 = function(offset, value) {
throw new Error("Unsupported 8-bit access!");
};
EEPROMSavedata.prototype.store16 = function(offset, value) {
switch (this.command) {
// Read header
case this.COMMAND_NULL:
default:
this.command = value & 0x1;
break;
case this.COMMAND_PENDING:
this.command <<= 1;
this.command |= value & 0x1;
if (this.command == this.COMMAND_WRITE) {
if (!this.realSize) {
var bits = this.dma.count - 67;
this.realSize = 8 << bits;
this.addressBits = bits;
}
this.commandBitsRemaining = this.addressBits + 64 + 1;
this.writeAddress = 0;
} else {
if (!this.realSize) {
var bits = this.dma.count - 3;
this.realSize = 8 << bits;
this.addressBits = bits;
}
this.commandBitsRemaining = this.addressBits + 1;
this.readAddress = 0;
}
break;
// Do commands
case this.COMMAND_WRITE:
// Write
if (--this.commandBitsRemaining > 64) {
this.writeAddress <<= 1;
this.writeAddress |= (value & 0x1) << 6;
} else if (this.commandBitsRemaining <= 0) {
this.command = this.COMMAND_NULL;
this.writePending = true;
} else {
var current = this.view.getUint8(this.writeAddress >> 3);
current &= ~(1 << (0x7 - (this.writeAddress & 0x7)));
current |= (value & 0x1) << (0x7 - (this.writeAddress & 0x7));
this.view.setUint8(this.writeAddress >> 3, current);
++this.writeAddress;
}
break;
case this.COMMAND_READ_PENDING:
// Read
if (--this.commandBitsRemaining > 0) {
this.readAddress <<= 1;
if (value & 0x1) {
this.readAddress |= 0x40;
}
} else {
this.readBitsRemaining = 68;
this.command = this.COMMAND_READ;
}
break;
}
};
EEPROMSavedata.prototype.store32 = function(offset, value) {
throw new Error("Unsupported 32-bit access!");
};
EEPROMSavedata.prototype.replaceData = function(memory) {
MemoryView.prototype.replaceData.call(this, memory, 0);
};