1 module gbaid.gba.cpu; 2 3 import gbaid.util; 4 5 import gbaid.gba.register; 6 import gbaid.gba.memory; 7 import gbaid.gba.arm; 8 import gbaid.gba.thumb; 9 10 private enum uint AVERAGE_CPI = 2; 11 12 public class ARM7TDMI { 13 private MemoryBus* memory; 14 private Registers registers; 15 private bool haltSignal = false; 16 private bool irqSignal = false; 17 private int instruction; 18 private int decoded; 19 20 public this(MemoryBus* memory, uint entryPointAddress = 0x0) { 21 this.memory = memory; 22 // Initialize to ARM in system mode 23 registers.setFlag(CPSRFlag.T, Set.ARM); 24 registers.setMode(Mode.SYSTEM); 25 // Initialize the stack pointers 26 registers.set(Mode.SUPERVISOR, Register.SP, 0x3007FE0); 27 registers.set(Mode.IRQ, Register.SP, 0x3007FA0); 28 registers.set(Mode.USER, Register.SP, 0x3007F00); 29 // Set first instruction 30 registers.setPC(entryPointAddress); 31 // Branch to instruction 32 branch(); 33 } 34 35 public void halt(bool state) { 36 haltSignal = state; 37 } 38 39 public void irq(bool state) { 40 irqSignal = state; 41 } 42 43 public bool halted() { 44 return irqSignal; 45 } 46 47 public bool inIRQ() { 48 return irqSignal; 49 } 50 51 public int getProgramCounter() { 52 return registers.getExecutedPC(); 53 } 54 55 public int getPreFetch() { 56 return instruction; 57 } 58 59 public int getNextInstruction() { 60 return decoded; 61 } 62 63 public size_t emulate(size_t cycles) { 64 debug (outputInstructions) { 65 scope (failure) { 66 registers.dumpInstructions(); 67 } 68 } 69 // Discard all the cycles if halted 70 if (haltSignal) { 71 return 0; 72 } 73 // Otherwise use up 2 cycles per instruction 74 while (cycles >= AVERAGE_CPI) { 75 // Take the cycles for the instruction 76 cycles -= AVERAGE_CPI; 77 // Check for an IRQ 78 if (irqSignal && !registers.getFlag(CPSRFlag.I)) { 79 // Branch to the handler 80 branchIRQ(); 81 continue; 82 } 83 // Fetch the next instruction in the pipeline 84 int nextInstruction = fetchInstruction(); 85 // "Decode" the second instruction in the pipeline (we don't need to actually do anything) 86 int nextDecoded = instruction; 87 instruction = nextInstruction; 88 // Execute the last instruction in the pipeline 89 final switch (registers.instructionSet) { 90 case Set.ARM: 91 executeARMInstruction(®isters, memory, decoded); 92 break; 93 case Set.THUMB: 94 executeTHUMBInstruction(®isters, memory, decoded); 95 break; 96 } 97 decoded = nextDecoded; 98 // Then go to the next instruction 99 if (registers.wasPCModified()) { 100 branch(); 101 } else { 102 registers.incrementPC(); 103 } 104 // Discard all the cycles if the instruction caused a halt 105 if (haltSignal) { 106 return 0; 107 } 108 } 109 return cycles; 110 } 111 112 private void branch() { 113 // fetch first instruction 114 int firstInstruction = fetchInstruction(); 115 registers.incrementPC(); 116 // fetch second 117 instruction = fetchInstruction(); 118 registers.incrementPC(); 119 // "decode" first 120 decoded = firstInstruction; 121 } 122 123 private int fetchInstruction() { 124 final switch (registers.instructionSet) { 125 case Set.ARM: 126 return memory.get!int(registers.getPC()); 127 case Set.THUMB: 128 return memory.get!short(registers.getPC()).mirror(); 129 } 130 } 131 132 private void branchIRQ() { 133 registers.setSPSR(Mode.IRQ, registers.getCPSR()); 134 registers.set(Mode.IRQ, Register.LR, registers.getExecutedPC() + 4); 135 registers.setPC(0x18); 136 registers.setFlag(CPSRFlag.I, 1); 137 registers.setFlag(CPSRFlag.T, Set.ARM); 138 registers.setMode(Mode.IRQ); 139 branch(); 140 } 141 }