mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-02-24 07:02:27 +01:00
byuu says: Changelog: - gba/cpu: massive code cleanup effort - gba/cpu: DMA can run in between active instructions¹ - gba/cpu: added two-cycle startup delay between DMA activation and DMA transfers² - processor/spc700: BBC, BBC, CBNE cycle 4 is an idle cycle - processor/spc700: ADDW, SUBW, MOVW (read) cycle 4 is an idle cycle ¹: unfortunately, this causes yet another performance penalty for the poor GBA core =( Also, I think I may have missed disabling DMAs while the CPU is stopped. I'll fix that in the next WIP. ²: I put the waiting counter decrement at the wrong place, so this doesn't actually work. Needs to be more like this: auto CPU::step(uint clocks) -> void { for(auto _ : range(clocks)) { for(auto& timer : this->timer) timer.run(); for(auto& dma : this->dma) if(dma.active && dma.waiting) dma.waiting--; context.clock++; } ... auto CPU::DMA::run() -> bool { if(cpu.stopped() || !active || waiting) return false; transfer(); if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id; if(drq && id == 3) cpu.irq.flag |= CPU::Interrupt::Cartridge; return true; } Of course, the real fix will be restructuring how DMA works, so that it's always running in parallel with the CPU instead of this weird design where it tries to run all channels in some kind of loop until no channels are active anymore whenever one channel is activated. Not really sure how to design that yet, however.
427 lines
13 KiB
C++
427 lines
13 KiB
C++
auto CPU::readIO(uint32 addr) -> uint8 {
|
|
auto dma = [&]() -> DMA& { return this->dma[addr / 12 & 3]; };
|
|
auto timer = [&]() -> Timer& { return this->timer[addr.bits(2,3)]; };
|
|
|
|
switch(addr) {
|
|
|
|
//DMA0CNT_L, DMA1CNT_L, DMA2CNT_L, DMA3CNT_L
|
|
case 0x0400'00b8: case 0x0400'00c4: case 0x0400'00d0: case 0x0400'00dc: return 0x00;
|
|
case 0x0400'00b9: case 0x0400'00c5: case 0x0400'00d1: case 0x0400'00dd: return 0x00;
|
|
|
|
//DMA0CNT_H, DMA1CNT_H, DMA2CNT_H, DMA3CNT_H
|
|
case 0x0400'00ba: case 0x0400'00c6: case 0x0400'00d2: case 0x0400'00de: return (
|
|
dma().targetMode << 5
|
|
| dma().sourceMode.bit(0) << 7
|
|
);
|
|
case 0x0400'00bb: case 0x0400'00c7: case 0x0400'00d3: case 0x0400'00df: return (
|
|
dma().sourceMode.bit(1) << 0
|
|
| dma().repeat << 1
|
|
| dma().size << 2
|
|
| dma().drq << 3
|
|
| dma().timingMode << 4
|
|
| dma().irq << 6
|
|
| dma().enable << 7
|
|
);
|
|
|
|
//TM0CNT_L, TM1CNT_L, TM2CNT_L, TM3CNT_L
|
|
case 0x0400'0100: case 0x0400'0104: case 0x0400'0108: case 0x0400'010c: return timer().period.byte(0);
|
|
case 0x0400'0101: case 0x0400'0105: case 0x0400'0109: case 0x0400'010d: return timer().period.byte(1);
|
|
|
|
//TM0CNT_H, TM1CNT_H, TM2CNT_H, TM3CNT_H
|
|
case 0x0400'0102: case 0x0400'0106: case 0x0400'010a: case 0x0400'010e: return (
|
|
timer().frequency << 0
|
|
| timer().cascade << 2
|
|
| timer().irq << 6
|
|
| timer().enable << 7
|
|
);
|
|
case 0x0400'0103: case 0x0400'0107: case 0x0400'010b: case 0x0400'010f: return 0;
|
|
|
|
//SIOMULTI0 (SIODATA32_L), SIOMULTI1 (SIODATA32_H), SIOMULTI2, SIOMULTI3
|
|
case 0x0400'0120: case 0x0400'0122: case 0x0400'0124: case 0x0400'0126: {
|
|
if(auto data = player.read()) return data().byte(addr.bits(0,1));
|
|
return serial.data[addr.bits(1,2)].byte(0);
|
|
}
|
|
case 0x0400'0121: case 0x0400'0123: case 0x0400'0125: case 0x0400'0127: {
|
|
if(auto data = player.read()) return data().byte(addr.bits(0,1));
|
|
return serial.data[addr.bits(1,2)].byte(1);
|
|
}
|
|
|
|
//SIOCNT
|
|
case 0x0400'0128: return (
|
|
serial.shiftClockSelect << 0
|
|
| serial.shiftClockFrequency << 1
|
|
| serial.transferEnableReceive << 2
|
|
| serial.transferEnableSend << 3
|
|
| serial.startBit << 7
|
|
);
|
|
case 0x0400'0129: return (
|
|
serial.transferLength << 4
|
|
| serial.irqEnable << 6
|
|
);
|
|
|
|
//SIOMLT_SEND (SIODATA8)
|
|
case 0x0400'012a: return serial.data8;
|
|
case 0x0400'012b: return 0;
|
|
|
|
//KEYINPUT
|
|
case 0x04000130: {
|
|
static const uint landscape[] = {5, 4, 8, 9, 3, 2, 0, 1};
|
|
static const uint portrait[] = {5, 4, 8, 9, 0, 1, 2, 3};
|
|
auto lookup = !settings.rotateLeft ? landscape : portrait;
|
|
if(auto result = player.keyinput()) return result() >> 0;
|
|
uint8 result = 0;
|
|
for(uint n = 0; n < 8; n++) result |= platform->inputPoll(0, 0, lookup[n]) << n;
|
|
if((result & 0xc0) == 0xc0) result &= (uint8)~0xc0; //up+down cannot be pressed simultaneously
|
|
if((result & 0x30) == 0x30) result &= (uint8)~0x30; //left+right cannot be pressed simultaneously
|
|
return result ^ 0xff;
|
|
}
|
|
case 0x04000131: {
|
|
if(auto result = player.keyinput()) return result() >> 8;
|
|
uint8 result = 0;
|
|
result |= platform->inputPoll(0, 0, 7) << 0;
|
|
result |= platform->inputPoll(0, 0, 6) << 1;
|
|
return result ^ 0x03;
|
|
}
|
|
|
|
//KEYCNT
|
|
case 0x0400'0132: return (
|
|
keypad.flag[0] << 0
|
|
| keypad.flag[1] << 1
|
|
| keypad.flag[2] << 2
|
|
| keypad.flag[3] << 3
|
|
| keypad.flag[4] << 4
|
|
| keypad.flag[5] << 5
|
|
| keypad.flag[6] << 6
|
|
| keypad.flag[7] << 7
|
|
);
|
|
case 0x0400'0133: return (
|
|
keypad.flag[8] << 0
|
|
| keypad.flag[9] << 1
|
|
| keypad.enable << 6
|
|
| keypad.condition << 7
|
|
);
|
|
|
|
//RCNT
|
|
case 0x0400'0134: return (
|
|
joybus.sc << 0
|
|
| joybus.sd << 1
|
|
| joybus.si << 2
|
|
| joybus.so << 3
|
|
| joybus.scMode << 4
|
|
| joybus.sdMode << 5
|
|
| joybus.siMode << 6
|
|
| joybus.soMode << 7
|
|
);
|
|
case 0x0400'0135: return (
|
|
joybus.siIRQEnable << 0
|
|
| joybus.mode << 6
|
|
);
|
|
|
|
//JOYCNT
|
|
case 0x0400'0140: return (
|
|
joybus.resetSignal << 0
|
|
| joybus.receiveComplete << 1
|
|
| joybus.sendComplete << 2
|
|
| joybus.resetIRQEnable << 6
|
|
);
|
|
case 0x0400'0141: return 0;
|
|
case 0x0400'0142: return 0;
|
|
case 0x0400'0143: return 0;
|
|
|
|
//JOY_RECV_L, JOY_RECV_H
|
|
case 0x0400'0150: return joybus.receive.byte(0);
|
|
case 0x0400'0151: return joybus.receive.byte(1);
|
|
case 0x0400'0152: return joybus.receive.byte(2);
|
|
case 0x0400'0153: return joybus.receive.byte(3);
|
|
|
|
//JOY_TRANS_L, JOY_TRANS_H
|
|
case 0x0400'0154: return joybus.transmit.byte(0);
|
|
case 0x0400'0155: return joybus.transmit.byte(1);
|
|
case 0x0400'0156: return joybus.transmit.byte(2);
|
|
case 0x0400'0157: return joybus.transmit.byte(3);
|
|
|
|
//JOYSTAT
|
|
case 0x0400'0158: return (
|
|
joybus.receiveFlag << 1
|
|
| joybus.sendFlag << 3
|
|
| joybus.generalFlag << 4
|
|
);
|
|
case 0x0400'0159: return 0;
|
|
case 0x0400'015a: return 0;
|
|
case 0x0400'015b: return 0;
|
|
|
|
//IE
|
|
case 0x0400'0200: return irq.enable.byte(0);
|
|
case 0x0400'0201: return irq.enable.byte(1);
|
|
|
|
//IF
|
|
case 0x0400'0202: return irq.flag.byte(0);
|
|
case 0x0400'0203: return irq.flag.byte(1);
|
|
|
|
//WAITCNT
|
|
case 0x0400'0204: return (
|
|
wait.nwait[3] << 0
|
|
| wait.nwait[0] << 2
|
|
| wait.swait[0] << 4
|
|
| wait.nwait[1] << 5
|
|
| wait.swait[1] << 7
|
|
);
|
|
case 0x0400'0205: return (
|
|
wait.nwait[2] << 0
|
|
| wait.swait[2] << 2
|
|
| wait.phi << 3
|
|
| wait.prefetch << 6
|
|
| wait.gameType << 7
|
|
);
|
|
|
|
//IME
|
|
case 0x0400'0208: return irq.ime;
|
|
case 0x0400'0209: return 0;
|
|
|
|
//POSTFLG + HALTCNT
|
|
case 0x0400'0300: return context.booted;
|
|
case 0x0400'0301: return 0;
|
|
|
|
//MEMCNT_L
|
|
case 0x0400'0800: return (
|
|
memory.disable << 0
|
|
| memory.unknown1 << 1
|
|
| memory.ewram << 5
|
|
);
|
|
case 0x0400'0801: return 0;
|
|
|
|
//MEMCNT_H
|
|
case 0x0400'0802: return 0;
|
|
case 0x0400'0803: return (
|
|
memory.ewramWait << 0
|
|
| memory.unknown2 << 4
|
|
);
|
|
|
|
}
|
|
|
|
return cpu.pipeline.fetch.instruction.byte(addr & 1);
|
|
}
|
|
|
|
auto CPU::writeIO(uint32 addr, uint8 data) -> void {
|
|
auto dma = [&]() -> DMA& { return this->dma[addr / 12 & 3]; };
|
|
auto timer = [&]() -> Timer& { return this->timer[addr.bits(2,3)]; };
|
|
|
|
switch(addr) {
|
|
|
|
//DMA0SAD, DMA1SAD, DMA2SAD, DMA3SAD
|
|
case 0x0400'00b0: case 0x0400'00bc: case 0x0400'00c8: case 0x0400'00d4: dma().source.byte(0) = data; return;
|
|
case 0x0400'00b1: case 0x0400'00bd: case 0x0400'00c9: case 0x0400'00d5: dma().source.byte(1) = data; return;
|
|
case 0x0400'00b2: case 0x0400'00be: case 0x0400'00ca: case 0x0400'00d6: dma().source.byte(2) = data; return;
|
|
case 0x0400'00b3: case 0x0400'00bf: case 0x0400'00cb: case 0x0400'00d7: dma().source.byte(3) = data; return;
|
|
|
|
//DMA0DAD, DMA1DAD, DMA2DAD, DMA3DAD
|
|
case 0x0400'00b4: case 0x0400'00c0: case 0x0400'00cc: case 0x0400'00d8: dma().target.byte(0) = data; return;
|
|
case 0x0400'00b5: case 0x0400'00c1: case 0x0400'00cd: case 0x0400'00d9: dma().target.byte(1) = data; return;
|
|
case 0x0400'00b6: case 0x0400'00c2: case 0x0400'00ce: case 0x0400'00da: dma().target.byte(2) = data; return;
|
|
case 0x0400'00b7: case 0x0400'00c3: case 0x0400'00cf: case 0x0400'00db: dma().target.byte(3) = data; return;
|
|
|
|
//DMA0CNT_L, DMA1CNT_L, DMA2CNT_L, DMA3CNT_L
|
|
case 0x0400'00b8: case 0x0400'00c4: case 0x0400'00d0: case 0x0400'00dc: dma().length.byte(0) = data; return;
|
|
case 0x0400'00b9: case 0x0400'00c5: case 0x0400'00d1: case 0x0400'00dd: dma().length.byte(1) = data; return;
|
|
|
|
//DMA0CNT_H, DMA1CNT_H, DMA2CNT_H, DMA3CNT_H
|
|
case 0x0400'00ba: case 0x0400'00c6: case 0x0400'00d2: case 0x0400'00de:
|
|
dma().targetMode = data.bits(5,6);
|
|
dma().sourceMode.bit(0) = data.bit (7);
|
|
return;
|
|
case 0x0400'00bb: case 0x0400'00c7: case 0x0400'00d3: case 0x0400'00df: {
|
|
bool enable = dma().enable;
|
|
if(addr != 0x0400'00df) data.bit(3) = 0; //gamepad DRQ valid for DMA3 only
|
|
|
|
dma().sourceMode.bit(1) = data.bit (0);
|
|
dma().repeat = data.bit (1);
|
|
dma().size = data.bit (2);
|
|
dma().drq = data.bit (3);
|
|
dma().timingMode = data.bits(4,5);
|
|
dma().irq = data.bit (6);
|
|
dma().enable = data.bit (7);
|
|
|
|
if(!enable && dma().enable) { //0->1 transition
|
|
if(dma().timingMode == 0) {
|
|
dma().active = true; //immediate transfer mode
|
|
dma().waiting = 2;
|
|
}
|
|
dma().latch.target = dma().target;
|
|
dma().latch.source = dma().source;
|
|
dma().latch.length = dma().length;
|
|
} else if(!dma().enable) {
|
|
dma().active = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//TM0CNT_L, TM1CNT_L, TM2CNT_L, TM3CNT_L
|
|
case 0x0400'0100: case 0x0400'0104: case 0x0400'0108: case 0x0400'010c: timer().reload.byte(0) = data; return;
|
|
case 0x0400'0101: case 0x0400'0105: case 0x0400'0109: case 0x0400'010d: timer().reload.byte(1) = data; return;
|
|
|
|
//TM0CNT_H, TM1CNT_H, TM2CNT_H, TM3CNT_H
|
|
case 0x0400'0102: case 0x0400'0106: case 0x0400'010a: case 0x0400'010e: {
|
|
bool enable = timer().enable;
|
|
|
|
timer().frequency = data.bits(0,1);
|
|
timer().cascade = data.bit (2);
|
|
timer().irq = data.bit (6);
|
|
timer().enable = data.bit (7);
|
|
|
|
if(!enable && timer().enable) { //0->1 transition
|
|
timer().pending = true;
|
|
}
|
|
return;
|
|
}
|
|
case 0x0400'0103: case 0x0400'0107: case 0x0400'010b: case 0x0400'010f:
|
|
return;
|
|
|
|
//SIOMULTI0 (SIODATA32_L), SIOMULTI1 (SIODATA32_H), SIOMULTI2, SIOMULTI3
|
|
case 0x0400'0120: case 0x0400'0122: case 0x0400'0124: case 0x0400'0126:
|
|
player.write(addr.bits(0,1), data);
|
|
serial.data[addr.bits(1,2)].byte(0) = data;
|
|
return;
|
|
case 0x0400'0121: case 0x0400'0123: case 0x0400'0125: case 0x0400'0127:
|
|
player.write(addr.bits(0,1), data);
|
|
serial.data[addr.bits(1,2)].byte(1) = data;
|
|
return;
|
|
|
|
//SIOCNT
|
|
case 0x0400'0128:
|
|
serial.shiftClockSelect = data.bit(0);
|
|
serial.shiftClockFrequency = data.bit(1);
|
|
serial.transferEnableReceive = data.bit(2);
|
|
serial.transferEnableSend = data.bit(3);
|
|
serial.startBit = data.bit(7);
|
|
return;
|
|
case 0x0400'0129:
|
|
serial.transferLength = data.bit(4);
|
|
serial.irqEnable = data.bit(6);
|
|
return;
|
|
|
|
//SIOMLT_SEND (SIODATA8)
|
|
case 0x0400'012a: serial.data8 = data; return;
|
|
case 0x0400'012b: return;
|
|
|
|
//KEYCNT
|
|
case 0x0400'0132:
|
|
keypad.flag[0] = data.bit(0);
|
|
keypad.flag[1] = data.bit(1);
|
|
keypad.flag[2] = data.bit(2);
|
|
keypad.flag[3] = data.bit(3);
|
|
keypad.flag[4] = data.bit(4);
|
|
keypad.flag[5] = data.bit(5);
|
|
keypad.flag[6] = data.bit(6);
|
|
keypad.flag[7] = data.bit(7);
|
|
return;
|
|
case 0x0400'0133:
|
|
keypad.flag[8] = data.bit(0);
|
|
keypad.flag[9] = data.bit(1);
|
|
keypad.enable = data.bit(6);
|
|
keypad.condition = data.bit(7);
|
|
return;
|
|
|
|
//RCNT
|
|
case 0x0400'0134:
|
|
joybus.sc = data.bit(0);
|
|
joybus.sd = data.bit(1);
|
|
joybus.si = data.bit(2);
|
|
joybus.so = data.bit(3);
|
|
joybus.scMode = data.bit(4);
|
|
joybus.sdMode = data.bit(5);
|
|
joybus.siMode = data.bit(6);
|
|
joybus.soMode = data.bit(7);
|
|
return;
|
|
case 0x0400'0135:
|
|
joybus.siIRQEnable = data.bit (0);
|
|
joybus.mode = data.bits(6,7);
|
|
return;
|
|
|
|
//JOYCNT
|
|
case 0x0400'0140:
|
|
joybus.resetSignal = data.bit(0);
|
|
joybus.receiveComplete = data.bit(1);
|
|
joybus.sendComplete = data.bit(2);
|
|
joybus.resetIRQEnable = data.bit(6);
|
|
return;
|
|
case 0x0400'0141: return;
|
|
case 0x0400'0142: return;
|
|
case 0x0400'0143: return;
|
|
|
|
//JOY_RECV_L
|
|
//JOY_RECV_H
|
|
case 0x0400'0150: joybus.receive.byte(0) = data; return;
|
|
case 0x0400'0151: joybus.receive.byte(1) = data; return;
|
|
case 0x0400'0152: joybus.receive.byte(2) = data; return;
|
|
case 0x0400'0153: joybus.receive.byte(3) = data; return;
|
|
|
|
//JOY_TRANS_L
|
|
//JOY_TRANS_H
|
|
case 0x0400'0154: joybus.transmit.byte(0) = data; return;
|
|
case 0x0400'0155: joybus.transmit.byte(1) = data; return;
|
|
case 0x0400'0156: joybus.transmit.byte(2) = data; return;
|
|
case 0x0400'0157: joybus.transmit.byte(3) = data; return;
|
|
|
|
//JOYSTAT
|
|
case 0x0400'0158:
|
|
joybus.receiveFlag = data.bit (1);
|
|
joybus.sendFlag = data.bit (3);
|
|
joybus.generalFlag = data.bits(4,5);
|
|
return;
|
|
case 0x0400'0159: return;
|
|
|
|
//IE
|
|
case 0x0400'0200: irq.enable.byte(0) = data; return;
|
|
case 0x0400'0201: irq.enable.byte(1) = data; return;
|
|
|
|
//IF
|
|
case 0x0400'0202: irq.flag.byte(0) = irq.flag.byte(0) & ~data; return;
|
|
case 0x0400'0203: irq.flag.byte(1) = irq.flag.byte(1) & ~data; return;
|
|
|
|
//WAITCNT
|
|
case 0x0400'0204:
|
|
wait.swait[3] = data.bit (0); //todo: is this correct?
|
|
wait.nwait[3] = data.bits(0,1);
|
|
wait.nwait[0] = data.bits(2,3);
|
|
wait.swait[0] = data.bit (4);
|
|
wait.nwait[1] = data.bits(5,6);
|
|
wait.swait[1] = data.bit (7);
|
|
return;
|
|
case 0x0400'0205:
|
|
wait.nwait[2] = data.bits(0,1);
|
|
wait.swait[2] = data.bit (2);
|
|
wait.phi = data.bit (3);
|
|
wait.prefetch = data.bit (6);
|
|
//wait.gameType is read-only
|
|
return;
|
|
|
|
//IME
|
|
case 0x0400'0208: irq.ime = data.bit(0); return;
|
|
case 0x0400'0209: return;
|
|
|
|
//POSTFLG, HALTCNT
|
|
case 0x0400'0300:
|
|
if(data.bit(0)) context.booted = 1;
|
|
return;
|
|
case 0x0400'0301:
|
|
context.halted = data.bit(7) == 0;
|
|
context.stopped = data.bit(7) == 1;
|
|
return;
|
|
|
|
//MEMCNT_L
|
|
//MEMCNT_H
|
|
case 0x0400'0800:
|
|
memory.disable = data.bit (0);
|
|
memory.unknown1 = data.bits(1,3);
|
|
memory.ewram = data.bit (5);
|
|
return;
|
|
case 0x0400'0801: return;
|
|
case 0x0400'0802: return;
|
|
case 0x0400'0803:
|
|
memory.ewramWait = data.bits(0,3);
|
|
memory.unknown2 = data.bits(4,7);
|
|
return;
|
|
|
|
}
|
|
}
|