829 lines
20 KiB
JavaScript
829 lines
20 KiB
JavaScript
function GameBoyAdvanceIO() {
|
|
// Video
|
|
this.DISPCNT = 0x000;
|
|
this.GREENSWP = 0x002;
|
|
this.DISPSTAT = 0x004;
|
|
this.VCOUNT = 0x006;
|
|
this.BG0CNT = 0x008;
|
|
this.BG1CNT = 0x00A;
|
|
this.BG2CNT = 0x00C;
|
|
this.BG3CNT = 0x00E;
|
|
this.BG0HOFS = 0x010;
|
|
this.BG0VOFS = 0x012;
|
|
this.BG1HOFS = 0x014;
|
|
this.BG1VOFS = 0x016;
|
|
this.BG2HOFS = 0x018;
|
|
this.BG2VOFS = 0x01A;
|
|
this.BG3HOFS = 0x01C;
|
|
this.BG3VOFS = 0x01E;
|
|
this.BG2PA = 0x020;
|
|
this.BG2PB = 0x022;
|
|
this.BG2PC = 0x024;
|
|
this.BG2PD = 0x026;
|
|
this.BG2X_LO = 0x028;
|
|
this.BG2X_HI = 0x02A;
|
|
this.BG2Y_LO = 0x02C;
|
|
this.BG2Y_HI = 0x02E;
|
|
this.BG3PA = 0x030;
|
|
this.BG3PB = 0x032;
|
|
this.BG3PC = 0x034;
|
|
this.BG3PD = 0x036;
|
|
this.BG3X_LO = 0x038;
|
|
this.BG3X_HI = 0x03A;
|
|
this.BG3Y_LO = 0x03C;
|
|
this.BG3Y_HI = 0x03E;
|
|
this.WIN0H = 0x040;
|
|
this.WIN1H = 0x042;
|
|
this.WIN0V = 0x044;
|
|
this.WIN1V = 0x046;
|
|
this.WININ = 0x048;
|
|
this.WINOUT = 0x04A;
|
|
this.MOSAIC = 0x04C;
|
|
this.BLDCNT = 0x050;
|
|
this.BLDALPHA = 0x052;
|
|
this.BLDY = 0x054;
|
|
|
|
// Sound
|
|
this.SOUND1CNT_LO = 0x060;
|
|
this.SOUND1CNT_HI = 0x062;
|
|
this.SOUND1CNT_X = 0x064;
|
|
this.SOUND2CNT_LO = 0x068;
|
|
this.SOUND2CNT_HI = 0x06C;
|
|
this.SOUND3CNT_LO = 0x070;
|
|
this.SOUND3CNT_HI = 0x072;
|
|
this.SOUND3CNT_X = 0x074;
|
|
this.SOUND4CNT_LO = 0x078;
|
|
this.SOUND4CNT_HI = 0x07C;
|
|
this.SOUNDCNT_LO = 0x080;
|
|
this.SOUNDCNT_HI = 0x082;
|
|
this.SOUNDCNT_X = 0x084;
|
|
this.SOUNDBIAS = 0x088;
|
|
this.WAVE_RAM0_LO = 0x090;
|
|
this.WAVE_RAM0_HI = 0x092;
|
|
this.WAVE_RAM1_LO = 0x094;
|
|
this.WAVE_RAM1_HI = 0x096;
|
|
this.WAVE_RAM2_LO = 0x098;
|
|
this.WAVE_RAM2_HI = 0x09A;
|
|
this.WAVE_RAM3_LO = 0x09C;
|
|
this.WAVE_RAM3_HI = 0x09E;
|
|
this.FIFO_A_LO = 0x0A0;
|
|
this.FIFO_A_HI = 0x0A2;
|
|
this.FIFO_B_LO = 0x0A4;
|
|
this.FIFO_B_HI = 0x0A6;
|
|
|
|
// DMA
|
|
this.DMA0SAD_LO = 0x0B0;
|
|
this.DMA0SAD_HI = 0x0B2;
|
|
this.DMA0DAD_LO = 0x0B4;
|
|
this.DMA0DAD_HI = 0x0B6;
|
|
this.DMA0CNT_LO = 0x0B8;
|
|
this.DMA0CNT_HI = 0x0BA;
|
|
this.DMA1SAD_LO = 0x0BC;
|
|
this.DMA1SAD_HI = 0x0BE;
|
|
this.DMA1DAD_LO = 0x0C0;
|
|
this.DMA1DAD_HI = 0x0C2;
|
|
this.DMA1CNT_LO = 0x0C4;
|
|
this.DMA1CNT_HI = 0x0C6;
|
|
this.DMA2SAD_LO = 0x0C8;
|
|
this.DMA2SAD_HI = 0x0CA;
|
|
this.DMA2DAD_LO = 0x0CC;
|
|
this.DMA2DAD_HI = 0x0CE;
|
|
this.DMA2CNT_LO = 0x0D0;
|
|
this.DMA2CNT_HI = 0x0D2;
|
|
this.DMA3SAD_LO = 0x0D4;
|
|
this.DMA3SAD_HI = 0x0D6;
|
|
this.DMA3DAD_LO = 0x0D8;
|
|
this.DMA3DAD_HI = 0x0DA;
|
|
this.DMA3CNT_LO = 0x0DC;
|
|
this.DMA3CNT_HI = 0x0DE;
|
|
|
|
// Timers
|
|
this.TM0CNT_LO = 0x100;
|
|
this.TM0CNT_HI = 0x102;
|
|
this.TM1CNT_LO = 0x104;
|
|
this.TM1CNT_HI = 0x106;
|
|
this.TM2CNT_LO = 0x108;
|
|
this.TM2CNT_HI = 0x10A;
|
|
this.TM3CNT_LO = 0x10C;
|
|
this.TM3CNT_HI = 0x10E;
|
|
|
|
// SIO (note: some of these are repeated)
|
|
this.SIODATA32_LO = 0x120;
|
|
this.SIOMULTI0 = 0x120;
|
|
this.SIODATA32_HI = 0x122;
|
|
this.SIOMULTI1 = 0x122;
|
|
this.SIOMULTI2 = 0x124;
|
|
this.SIOMULTI3 = 0x126;
|
|
this.SIOCNT = 0x128;
|
|
this.SIOMLT_SEND = 0x12A;
|
|
this.SIODATA8 = 0x12A;
|
|
this.RCNT = 0x134;
|
|
this.JOYCNT = 0x140;
|
|
this.JOY_RECV = 0x150;
|
|
this.JOY_TRANS = 0x154;
|
|
this.JOYSTAT = 0x158;
|
|
|
|
// Keypad
|
|
this.KEYINPUT = 0x130;
|
|
this.KEYCNT = 0x132;
|
|
|
|
// Interrupts, etc
|
|
this.IE = 0x200;
|
|
this.IF = 0x202;
|
|
this.WAITCNT = 0x204;
|
|
this.IME = 0x208;
|
|
|
|
this.POSTFLG = 0x300;
|
|
this.HALTCNT = 0x301;
|
|
|
|
this.DEFAULT_DISPCNT = 0x0080;
|
|
this.DEFAULT_SOUNDBIAS = 0x200;
|
|
this.DEFAULT_BGPA = 1;
|
|
this.DEFAULT_BGPD = 1;
|
|
this.DEFAULT_RCNT = 0x8000;
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.clear = function() {
|
|
this.registers = new Uint16Array(this.cpu.mmu.SIZE_IO);
|
|
|
|
this.registers[this.DISPCNT >> 1] = this.DEFAULT_DISPCNT;
|
|
this.registers[this.SOUNDBIAS >> 1] = this.DEFAULT_SOUNDBIAS;
|
|
this.registers[this.BG2PA >> 1] = this.DEFAULT_BGPA;
|
|
this.registers[this.BG2PD >> 1] = this.DEFAULT_BGPD;
|
|
this.registers[this.BG3PA >> 1] = this.DEFAULT_BGPA;
|
|
this.registers[this.BG3PD >> 1] = this.DEFAULT_BGPD;
|
|
this.registers[this.RCNT >> 1] = this.DEFAULT_RCNT;
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.freeze = function() {
|
|
return {
|
|
'registers': Serializer.prefix(this.registers.buffer)
|
|
};
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.defrost = function(frost) {
|
|
this.registers = new Uint16Array(frost.registers);
|
|
// Video registers don't serialize themselves
|
|
// 将背景和背景类似的layer数据写回
|
|
for (var i = 0; i < this.registers.length; i += 2 ) {
|
|
this.store16(i, this.registers[i >> 1]);
|
|
}
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.load8 = function(offset) {
|
|
throw 'Unimplmeneted unaligned I/O access';
|
|
}
|
|
|
|
GameBoyAdvanceIO.prototype.load16 = function(offset) {
|
|
return (this.loadU16(offset) << 16) >> 16;
|
|
}
|
|
|
|
GameBoyAdvanceIO.prototype.load32 = function(offset) {
|
|
offset &= 0xFFFFFFFC;
|
|
switch (offset) {
|
|
case this.DMA0CNT_LO:
|
|
case this.DMA1CNT_LO:
|
|
case this.DMA2CNT_LO:
|
|
case this.DMA3CNT_LO:
|
|
return this.loadU16(offset | 2) << 16;
|
|
case this.IME:
|
|
return this.loadU16(offset) & 0xFFFF;
|
|
case this.JOY_RECV:
|
|
case this.JOY_TRANS:
|
|
this.core.STUB('Unimplemented JOY register read: 0x' + offset.toString(16));
|
|
return 0;
|
|
}
|
|
|
|
return this.loadU16(offset) | (this.loadU16(offset | 2) << 16);
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.loadU8 = function(offset) {
|
|
var odd = offset & 0x0001;
|
|
var value = this.loadU16(offset & 0xFFFE);
|
|
return (value >>> (odd << 3)) & 0xFF;
|
|
}
|
|
|
|
GameBoyAdvanceIO.prototype.loadU16 = function(offset) {
|
|
switch (offset) {
|
|
case this.DISPCNT:
|
|
case this.BG0CNT:
|
|
case this.BG1CNT:
|
|
case this.BG2CNT:
|
|
case this.BG3CNT:
|
|
case this.WININ:
|
|
case this.WINOUT:
|
|
case this.SOUND1CNT_LO:
|
|
case this.SOUND3CNT_LO:
|
|
case this.SOUNDCNT_LO:
|
|
case this.SOUNDCNT_HI:
|
|
case this.SOUNDBIAS:
|
|
case this.BLDCNT:
|
|
case this.BLDALPHA:
|
|
|
|
case this.TM0CNT_HI:
|
|
case this.TM1CNT_HI:
|
|
case this.TM2CNT_HI:
|
|
case this.TM3CNT_HI:
|
|
case this.DMA0CNT_HI:
|
|
case this.DMA1CNT_HI:
|
|
case this.DMA2CNT_HI:
|
|
case this.DMA3CNT_HI:
|
|
case this.RCNT:
|
|
case this.WAITCNT:
|
|
case this.IE:
|
|
case this.IF:
|
|
case this.IME:
|
|
case this.POSTFLG:
|
|
// Handled transparently by the written registers
|
|
break;
|
|
|
|
// Video
|
|
case this.DISPSTAT:
|
|
return this.registers[offset >> 1] | this.video.readDisplayStat();
|
|
case this.VCOUNT:
|
|
return this.video.vcount;
|
|
|
|
// Sound
|
|
case this.SOUND1CNT_HI:
|
|
case this.SOUND2CNT_LO:
|
|
return this.registers[offset >> 1] & 0xFFC0;
|
|
case this.SOUND1CNT_X:
|
|
case this.SOUND2CNT_HI:
|
|
case this.SOUND3CNT_X:
|
|
return this.registers[offset >> 1] & 0x4000;
|
|
case this.SOUND3CNT_HI:
|
|
return this.registers[offset >> 1] & 0xE000;
|
|
case this.SOUND4CNT_LO:
|
|
return this.registers[offset >> 1] & 0xFF00;
|
|
case this.SOUND4CNT_HI:
|
|
return this.registers[offset >> 1] & 0x40FF;
|
|
case this.SOUNDCNT_X:
|
|
this.core.STUB('Unimplemented sound register read: SOUNDCNT_X');
|
|
return this.registers[offset >> 1] | 0x0000;
|
|
|
|
// Timers
|
|
case this.TM0CNT_LO:
|
|
return this.cpu.irq.timerRead(0);
|
|
case this.TM1CNT_LO:
|
|
return this.cpu.irq.timerRead(1);
|
|
case this.TM2CNT_LO:
|
|
return this.cpu.irq.timerRead(2);
|
|
case this.TM3CNT_LO:
|
|
return this.cpu.irq.timerRead(3);
|
|
|
|
// SIO
|
|
case this.SIOCNT:
|
|
return this.sio.readSIOCNT();
|
|
|
|
case this.KEYINPUT:
|
|
this.keypad.pollGamepads();
|
|
return this.keypad.currentDown;
|
|
case this.KEYCNT:
|
|
this.core.STUB('Unimplemented I/O register read: KEYCNT');
|
|
return 0;
|
|
|
|
case this.BG0HOFS:
|
|
case this.BG0VOFS:
|
|
case this.BG1HOFS:
|
|
case this.BG1VOFS:
|
|
case this.BG2HOFS:
|
|
case this.BG2VOFS:
|
|
case this.BG3HOFS:
|
|
case this.BG3VOFS:
|
|
case this.BG2PA:
|
|
case this.BG2PB:
|
|
case this.BG2PC:
|
|
case this.BG2PD:
|
|
case this.BG3PA:
|
|
case this.BG3PB:
|
|
case this.BG3PC:
|
|
case this.BG3PD:
|
|
case this.BG2X_LO:
|
|
case this.BG2X_HI:
|
|
case this.BG2Y_LO:
|
|
case this.BG2Y_HI:
|
|
case this.BG3X_LO:
|
|
case this.BG3X_HI:
|
|
case this.BG3Y_LO:
|
|
case this.BG3Y_HI:
|
|
case this.WIN0H:
|
|
case this.WIN1H:
|
|
case this.WIN0V:
|
|
case this.WIN1V:
|
|
case this.BLDY:
|
|
case this.DMA0SAD_LO:
|
|
case this.DMA0SAD_HI:
|
|
case this.DMA0DAD_LO:
|
|
case this.DMA0DAD_HI:
|
|
case this.DMA0CNT_LO:
|
|
case this.DMA1SAD_LO:
|
|
case this.DMA1SAD_HI:
|
|
case this.DMA1DAD_LO:
|
|
case this.DMA1DAD_HI:
|
|
case this.DMA1CNT_LO:
|
|
case this.DMA2SAD_LO:
|
|
case this.DMA2SAD_HI:
|
|
case this.DMA2DAD_LO:
|
|
case this.DMA2DAD_HI:
|
|
case this.DMA2CNT_LO:
|
|
case this.DMA3SAD_LO:
|
|
case this.DMA3SAD_HI:
|
|
case this.DMA3DAD_LO:
|
|
case this.DMA3DAD_HI:
|
|
case this.DMA3CNT_LO:
|
|
case this.FIFO_A_LO:
|
|
case this.FIFO_A_HI:
|
|
case this.FIFO_B_LO:
|
|
case this.FIFO_B_HI:
|
|
this.core.WARN('Read for write-only register: 0x' + offset.toString(16));
|
|
return this.core.mmu.badMemory.loadU16(0);
|
|
|
|
case this.MOSAIC:
|
|
this.core.WARN('Read for write-only register: 0x' + offset.toString(16));
|
|
return 0;
|
|
|
|
case this.SIOMULTI0:
|
|
case this.SIOMULTI1:
|
|
case this.SIOMULTI2:
|
|
case this.SIOMULTI3:
|
|
return this.sio.read((offset - this.SIOMULTI0) >> 1);
|
|
|
|
case this.SIODATA8:
|
|
this.core.STUB('Unimplemented SIO register read: 0x' + offset.toString(16));
|
|
return 0;
|
|
case this.JOYCNT:
|
|
case this.JOYSTAT:
|
|
this.core.STUB('Unimplemented JOY register read: 0x' + offset.toString(16));
|
|
return 0;
|
|
|
|
default:
|
|
this.core.WARN('Bad I/O register read: 0x' + offset.toString(16));
|
|
return this.core.mmu.badMemory.loadU16(0);
|
|
}
|
|
return this.registers[offset >> 1];
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.store8 = function(offset, value) {
|
|
switch (offset) {
|
|
case this.WININ:
|
|
this.value & 0x3F;
|
|
break;
|
|
case this.WININ | 1:
|
|
this.value & 0x3F;
|
|
break;
|
|
case this.WINOUT:
|
|
this.value & 0x3F;
|
|
break;
|
|
case this.WINOUT | 1:
|
|
this.value & 0x3F;
|
|
break;
|
|
case this.SOUND1CNT_LO:
|
|
case this.SOUND1CNT_LO | 1:
|
|
case this.SOUND1CNT_HI:
|
|
case this.SOUND1CNT_HI | 1:
|
|
case this.SOUND1CNT_X:
|
|
case this.SOUND1CNT_X | 1:
|
|
case this.SOUND2CNT_LO:
|
|
case this.SOUND2CNT_LO | 1:
|
|
case this.SOUND2CNT_HI:
|
|
case this.SOUND2CNT_HI | 1:
|
|
case this.SOUND3CNT_LO:
|
|
case this.SOUND3CNT_LO | 1:
|
|
case this.SOUND3CNT_HI:
|
|
case this.SOUND3CNT_HI | 1:
|
|
case this.SOUND3CNT_X:
|
|
case this.SOUND3CNT_X | 1:
|
|
case this.SOUND4CNT_LO:
|
|
case this.SOUND4CNT_LO | 1:
|
|
case this.SOUND4CNT_HI:
|
|
case this.SOUND4CNT_HI | 1:
|
|
case this.SOUNDCNT_LO:
|
|
case this.SOUNDCNT_LO | 1:
|
|
case this.SOUNDCNT_X:
|
|
case this.IF:
|
|
case this.IME:
|
|
break;
|
|
case this.SOUNDBIAS | 1:
|
|
this.STUB_REG('sound', offset);
|
|
break;
|
|
case this.HALTCNT:
|
|
value &= 0x80;
|
|
if (!value) {
|
|
this.core.irq.halt();
|
|
} else {
|
|
this.core.STUB('Stop');
|
|
}
|
|
return;
|
|
default:
|
|
this.STUB_REG('8-bit I/O', offset);
|
|
break;
|
|
}
|
|
|
|
if (offset & 1) {
|
|
value <<= 8;
|
|
value |= (this.registers[offset >> 1] & 0x00FF);
|
|
} else {
|
|
value &= 0x00FF;
|
|
value |= (this.registers[offset >> 1] & 0xFF00);
|
|
}
|
|
this.store16(offset & 0xFFFFFFE, value);
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.store16 = function(offset, value) {
|
|
// console.log('GameBoyAdvanceIO store16', offset, value);
|
|
switch (offset) {
|
|
// Video
|
|
case this.DISPCNT:
|
|
this.video.renderPath.writeDisplayControl(value);
|
|
break;
|
|
case this.DISPSTAT:
|
|
value &= this.video.DISPSTAT_MASK;
|
|
this.video.writeDisplayStat(value);
|
|
break;
|
|
case this.BG0CNT:
|
|
this.video.renderPath.writeBackgroundControl(0, value);
|
|
break;
|
|
case this.BG1CNT:
|
|
this.video.renderPath.writeBackgroundControl(1, value);
|
|
break;
|
|
case this.BG2CNT:
|
|
this.video.renderPath.writeBackgroundControl(2, value);
|
|
break;
|
|
case this.BG3CNT:
|
|
this.video.renderPath.writeBackgroundControl(3, value);
|
|
break;
|
|
case this.BG0HOFS:
|
|
this.video.renderPath.writeBackgroundHOffset(0, value);
|
|
break;
|
|
case this.BG0VOFS:
|
|
this.video.renderPath.writeBackgroundVOffset(0, value);
|
|
break;
|
|
case this.BG1HOFS:
|
|
this.video.renderPath.writeBackgroundHOffset(1, value);
|
|
break;
|
|
case this.BG1VOFS:
|
|
this.video.renderPath.writeBackgroundVOffset(1, value);
|
|
break;
|
|
case this.BG2HOFS:
|
|
this.video.renderPath.writeBackgroundHOffset(2, value);
|
|
break;
|
|
case this.BG2VOFS:
|
|
this.video.renderPath.writeBackgroundVOffset(2, value);
|
|
break;
|
|
case this.BG3HOFS:
|
|
this.video.renderPath.writeBackgroundHOffset(3, value);
|
|
break;
|
|
case this.BG3VOFS:
|
|
this.video.renderPath.writeBackgroundVOffset(3, value);
|
|
break;
|
|
case this.BG2X_LO:
|
|
this.video.renderPath.writeBackgroundRefX(2, (this.registers[(offset >> 1) | 1] << 16) | value);
|
|
break;
|
|
case this.BG2X_HI:
|
|
this.video.renderPath.writeBackgroundRefX(2, this.registers[(offset >> 1) ^ 1] | (value << 16));
|
|
break;
|
|
case this.BG2Y_LO:
|
|
this.video.renderPath.writeBackgroundRefY(2, (this.registers[(offset >> 1) | 1] << 16) | value);
|
|
break;
|
|
case this.BG2Y_HI:
|
|
this.video.renderPath.writeBackgroundRefY(2, this.registers[(offset >> 1) ^ 1] | (value << 16));
|
|
break;
|
|
case this.BG2PA:
|
|
this.video.renderPath.writeBackgroundParamA(2, value);
|
|
break;
|
|
case this.BG2PB:
|
|
this.video.renderPath.writeBackgroundParamB(2, value);
|
|
break;
|
|
case this.BG2PC:
|
|
this.video.renderPath.writeBackgroundParamC(2, value);
|
|
break;
|
|
case this.BG2PD:
|
|
this.video.renderPath.writeBackgroundParamD(2, value);
|
|
break;
|
|
case this.BG3X_LO:
|
|
this.video.renderPath.writeBackgroundRefX(3, (this.registers[(offset >> 1) | 1] << 16) | value);
|
|
break;
|
|
case this.BG3X_HI:
|
|
this.video.renderPath.writeBackgroundRefX(3, this.registers[(offset >> 1) ^ 1] | (value << 16));
|
|
break;
|
|
case this.BG3Y_LO:
|
|
this.video.renderPath.writeBackgroundRefY(3, (this.registers[(offset >> 1) | 1] << 16) | value);
|
|
break;
|
|
case this.BG3Y_HI:
|
|
this.video.renderPath.writeBackgroundRefY(3, this.registers[(offset >> 1) ^ 1] | (value << 16));
|
|
break;
|
|
case this.BG3PA:
|
|
this.video.renderPath.writeBackgroundParamA(3, value);
|
|
break;
|
|
case this.BG3PB:
|
|
this.video.renderPath.writeBackgroundParamB(3, value);
|
|
break;
|
|
case this.BG3PC:
|
|
this.video.renderPath.writeBackgroundParamC(3, value);
|
|
break;
|
|
case this.BG3PD:
|
|
this.video.renderPath.writeBackgroundParamD(3, value);
|
|
break;
|
|
case this.WIN0H:
|
|
this.video.renderPath.writeWin0H(value);
|
|
break;
|
|
case this.WIN1H:
|
|
this.video.renderPath.writeWin1H(value);
|
|
break;
|
|
case this.WIN0V:
|
|
this.video.renderPath.writeWin0V(value);
|
|
break;
|
|
case this.WIN1V:
|
|
this.video.renderPath.writeWin1V(value);
|
|
break;
|
|
case this.WININ:
|
|
value &= 0x3F3F;
|
|
this.video.renderPath.writeWinIn(value);
|
|
break;
|
|
case this.WINOUT:
|
|
value &= 0x3F3F;
|
|
this.video.renderPath.writeWinOut(value);
|
|
break;
|
|
case this.BLDCNT:
|
|
value &= 0x7FFF;
|
|
this.video.renderPath.writeBlendControl(value);
|
|
break;
|
|
case this.BLDALPHA:
|
|
value &= 0x1F1F;
|
|
this.video.renderPath.writeBlendAlpha(value);
|
|
break;
|
|
case this.BLDY:
|
|
value &= 0x001F;
|
|
this.video.renderPath.writeBlendY(value);
|
|
break;
|
|
case this.MOSAIC:
|
|
this.video.renderPath.writeMosaic(value);
|
|
break;
|
|
|
|
// Sound
|
|
case this.SOUND1CNT_LO:
|
|
value &= 0x007F;
|
|
this.audio.writeSquareChannelSweep(0, value);
|
|
break;
|
|
case this.SOUND1CNT_HI:
|
|
this.audio.writeSquareChannelDLE(0, value);
|
|
break;
|
|
case this.SOUND1CNT_X:
|
|
value &= 0xC7FF;
|
|
this.audio.writeSquareChannelFC(0, value);
|
|
value &= ~0x8000;
|
|
break;
|
|
case this.SOUND2CNT_LO:
|
|
this.audio.writeSquareChannelDLE(1, value);
|
|
break;
|
|
case this.SOUND2CNT_HI:
|
|
value &= 0xC7FF;
|
|
this.audio.writeSquareChannelFC(1, value);
|
|
value &= ~0x8000;
|
|
break;
|
|
case this.SOUND3CNT_LO:
|
|
value &= 0x00E0;
|
|
this.audio.writeChannel3Lo(value);
|
|
break;
|
|
case this.SOUND3CNT_HI:
|
|
value &= 0xE0FF;
|
|
this.audio.writeChannel3Hi(value);
|
|
break;
|
|
case this.SOUND3CNT_X:
|
|
value &= 0xC7FF;
|
|
this.audio.writeChannel3X(value);
|
|
value &= ~0x8000;
|
|
break;
|
|
case this.SOUND4CNT_LO:
|
|
value &= 0xFF3F;
|
|
this.audio.writeChannel4LE(value);
|
|
break;
|
|
case this.SOUND4CNT_HI:
|
|
value &= 0xC0FF;
|
|
this.audio.writeChannel4FC(value);
|
|
value &= ~0x8000;
|
|
break;
|
|
case this.SOUNDCNT_LO:
|
|
value &= 0xFF77;
|
|
this.audio.writeSoundControlLo(value);
|
|
break;
|
|
case this.SOUNDCNT_HI:
|
|
value &= 0xFF0F;
|
|
this.audio.writeSoundControlHi(value);
|
|
break;
|
|
case this.SOUNDCNT_X:
|
|
value &= 0x0080;
|
|
this.audio.writeEnable(value);
|
|
break;
|
|
case this.WAVE_RAM0_LO:
|
|
case this.WAVE_RAM0_HI:
|
|
case this.WAVE_RAM1_LO:
|
|
case this.WAVE_RAM1_HI:
|
|
case this.WAVE_RAM2_LO:
|
|
case this.WAVE_RAM2_HI:
|
|
case this.WAVE_RAM3_LO:
|
|
case this.WAVE_RAM3_HI:
|
|
this.audio.writeWaveData(offset - this.WAVE_RAM0_LO, value, 2);
|
|
break;
|
|
|
|
// DMA
|
|
case this.DMA0SAD_LO:
|
|
case this.DMA0DAD_LO:
|
|
case this.DMA1SAD_LO:
|
|
case this.DMA1DAD_LO:
|
|
case this.DMA2SAD_LO:
|
|
case this.DMA2DAD_LO:
|
|
case this.DMA3SAD_LO:
|
|
case this.DMA3DAD_LO:
|
|
this.store32(offset, (this.registers[(offset >> 1) + 1] << 16) | value);
|
|
return;
|
|
|
|
case this.DMA0SAD_HI:
|
|
case this.DMA0DAD_HI:
|
|
case this.DMA1SAD_HI:
|
|
case this.DMA1DAD_HI:
|
|
case this.DMA2SAD_HI:
|
|
case this.DMA2DAD_HI:
|
|
case this.DMA3SAD_HI:
|
|
case this.DMA3DAD_HI:
|
|
this.store32(offset - 2, this.registers[(offset >> 1) - 1] | (value << 16));
|
|
return;
|
|
|
|
case this.DMA0CNT_LO:
|
|
this.cpu.irq.dmaSetWordCount(0, value);
|
|
break;
|
|
case this.DMA0CNT_HI:
|
|
// The DMA registers need to set the values before writing the control, as writing the
|
|
// control can synchronously trigger a DMA transfer
|
|
this.registers[offset >> 1] = value & 0xFFE0;
|
|
this.cpu.irq.dmaWriteControl(0, value);
|
|
return;
|
|
case this.DMA1CNT_LO:
|
|
this.cpu.irq.dmaSetWordCount(1, value);
|
|
break;
|
|
case this.DMA1CNT_HI:
|
|
this.registers[offset >> 1] = value & 0xFFE0;
|
|
this.cpu.irq.dmaWriteControl(1, value);
|
|
return;
|
|
case this.DMA2CNT_LO:
|
|
this.cpu.irq.dmaSetWordCount(2, value);
|
|
break;
|
|
case this.DMA2CNT_HI:
|
|
this.registers[offset >> 1] = value & 0xFFE0;
|
|
this.cpu.irq.dmaWriteControl(2, value);
|
|
return;
|
|
case this.DMA3CNT_LO:
|
|
this.cpu.irq.dmaSetWordCount(3, value);
|
|
break;
|
|
case this.DMA3CNT_HI:
|
|
this.registers[offset >> 1] = value & 0xFFE0;
|
|
this.cpu.irq.dmaWriteControl(3, value);
|
|
return;
|
|
|
|
// Timers
|
|
case this.TM0CNT_LO:
|
|
this.cpu.irq.timerSetReload(0, value);
|
|
return;
|
|
case this.TM1CNT_LO:
|
|
this.cpu.irq.timerSetReload(1, value);
|
|
return;
|
|
case this.TM2CNT_LO:
|
|
this.cpu.irq.timerSetReload(2, value);
|
|
return;
|
|
case this.TM3CNT_LO:
|
|
this.cpu.irq.timerSetReload(3, value);
|
|
return;
|
|
|
|
case this.TM0CNT_HI:
|
|
value &= 0x00C7
|
|
this.cpu.irq.timerWriteControl(0, value);
|
|
break;
|
|
case this.TM1CNT_HI:
|
|
value &= 0x00C7
|
|
this.cpu.irq.timerWriteControl(1, value);
|
|
break;
|
|
case this.TM2CNT_HI:
|
|
value &= 0x00C7
|
|
this.cpu.irq.timerWriteControl(2, value);
|
|
break;
|
|
case this.TM3CNT_HI:
|
|
value &= 0x00C7
|
|
this.cpu.irq.timerWriteControl(3, value);
|
|
break;
|
|
|
|
// SIO
|
|
case this.SIOMULTI0:
|
|
case this.SIOMULTI1:
|
|
case this.SIOMULTI2:
|
|
case this.SIOMULTI3:
|
|
case this.SIODATA8:
|
|
this.STUB_REG('SIO', offset);
|
|
break;
|
|
case this.RCNT:
|
|
this.sio.setMode(((value >> 12) & 0xC) | ((this.registers[this.SIOCNT >> 1] >> 12) & 0x3));
|
|
this.sio.writeRCNT(value);
|
|
break;
|
|
case this.SIOCNT:
|
|
this.sio.setMode(((value >> 12) & 0x3) | ((this.registers[this.RCNT >> 1] >> 12) & 0xC));
|
|
this.sio.writeSIOCNT(value);
|
|
return;
|
|
case this.JOYCNT:
|
|
case this.JOYSTAT:
|
|
this.STUB_REG('JOY', offset);
|
|
break;
|
|
|
|
// Misc
|
|
case this.IE:
|
|
value &= 0x3FFF;
|
|
this.cpu.irq.setInterruptsEnabled(value);
|
|
break;
|
|
case this.IF:
|
|
this.cpu.irq.dismissIRQs(value);
|
|
return;
|
|
case this.WAITCNT:
|
|
value &= 0xDFFF;
|
|
this.cpu.mmu.adjustTimings(value);
|
|
break;
|
|
case this.IME:
|
|
value &= 0x0001;
|
|
this.cpu.irq.masterEnable(value);
|
|
break;
|
|
default:
|
|
this.STUB_REG('I/O', offset);
|
|
}
|
|
this.registers[offset >> 1] = value;
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.store32 = function(offset, value) {
|
|
switch (offset) {
|
|
case this.BG2X_LO:
|
|
value &= 0x0FFFFFFF;
|
|
this.video.renderPath.writeBackgroundRefX(2, value);
|
|
break;
|
|
case this.BG2Y_LO:
|
|
value &= 0x0FFFFFFF;
|
|
this.video.renderPath.writeBackgroundRefY(2, value);
|
|
break;
|
|
case this.BG3X_LO:
|
|
value &= 0x0FFFFFFF;
|
|
this.video.renderPath.writeBackgroundRefX(3, value);
|
|
break;
|
|
case this.BG3Y_LO:
|
|
value &= 0x0FFFFFFF;
|
|
this.video.renderPath.writeBackgroundRefY(3, value);
|
|
break;
|
|
case this.DMA0SAD_LO:
|
|
this.cpu.irq.dmaSetSourceAddress(0, value);
|
|
break;
|
|
case this.DMA0DAD_LO:
|
|
this.cpu.irq.dmaSetDestAddress(0, value);
|
|
break;
|
|
case this.DMA1SAD_LO:
|
|
this.cpu.irq.dmaSetSourceAddress(1, value);
|
|
break;
|
|
case this.DMA1DAD_LO:
|
|
this.cpu.irq.dmaSetDestAddress(1, value);
|
|
break;
|
|
case this.DMA2SAD_LO:
|
|
this.cpu.irq.dmaSetSourceAddress(2, value);
|
|
break;
|
|
case this.DMA2DAD_LO:
|
|
this.cpu.irq.dmaSetDestAddress(2, value);
|
|
break;
|
|
case this.DMA3SAD_LO:
|
|
this.cpu.irq.dmaSetSourceAddress(3, value);
|
|
break;
|
|
case this.DMA3DAD_LO:
|
|
this.cpu.irq.dmaSetDestAddress(3, value);
|
|
break;
|
|
case this.FIFO_A_LO:
|
|
this.audio.appendToFifoA(value);
|
|
return;
|
|
case this.FIFO_B_LO:
|
|
this.audio.appendToFifoB(value);
|
|
return;
|
|
|
|
// High bits of this write should be ignored
|
|
case this.IME:
|
|
this.store16(offset, value & 0xFFFF);
|
|
return;
|
|
case this.JOY_RECV:
|
|
case this.JOY_TRANS:
|
|
this.STUB_REG('JOY', offset);
|
|
return;
|
|
default:
|
|
this.store16(offset, value & 0xFFFF);
|
|
this.store16(offset | 2, value >>> 16);
|
|
return;
|
|
}
|
|
|
|
this.registers[offset >> 1] = value & 0xFFFF;
|
|
this.registers[(offset >> 1) + 1] = value >>> 16;
|
|
};
|
|
|
|
GameBoyAdvanceIO.prototype.invalidatePage = function(address) {};
|
|
|
|
GameBoyAdvanceIO.prototype.STUB_REG = function(type, offset) {
|
|
this.core.STUB('Unimplemented ' + type + ' register write: ' + offset.toString(16));
|
|
};
|