From 5cd5e799a6f710cdcb7a3535c7731a9f701dde17 Mon Sep 17 00:00:00 2001 From: zeromus Date: Sun, 3 Apr 2011 05:24:31 +0000 Subject: [PATCH] add 5% of the world's slowest ARM cpu emulator --- BizHawk.Emulation/BizHawk.Emulation.csproj | 16 +- BizHawk.Emulation/CPUs/ARM/ARM.CP.cs | 60 + BizHawk.Emulation/CPUs/ARM/ARM.Disassembly.cs | 464 + BizHawk.Emulation/CPUs/ARM/ARM.Execute.cs | 147 + .../CPUs/ARM/ARM.ExecuteArm.Unconditional.cs | 61 + BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.cs | 785 ++ BizHawk.Emulation/CPUs/ARM/ARM.ExecuteCore.cs | 998 ++ .../CPUs/ARM/ARM.ExecuteThumb.cs | 905 ++ BizHawk.Emulation/CPUs/ARM/ARM.Pseudocode.cs | 460 + BizHawk.Emulation/CPUs/ARM/ARM.cs | 211 + BizHawk.Emulation/CPUs/ARM/_.cs | 8270 +++++++++++++++++ BizHawk.Emulation/CPUs/ARM/decoder/decoder.cs | 179 + .../CPUs/ARM/decoder/scripting.cs | 136 + 13 files changed, 12691 insertions(+), 1 deletion(-) create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.CP.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.Disassembly.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.Execute.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.Unconditional.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.ExecuteCore.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.ExecuteThumb.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.Pseudocode.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/ARM.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/_.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/decoder/decoder.cs create mode 100644 BizHawk.Emulation/CPUs/ARM/decoder/scripting.cs diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index ee086355b6..85f2dc41ef 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -3,7 +3,7 @@ Debug AnyCPU - 9.0.21022 + 9.0.30729 2.0 {197D4314-8A9F-49BA-977D-54ACEFAEB6BA} Library @@ -40,6 +40,8 @@ False ..\BizHawk.MultiClient\LuaInterface.dll + + 3.5 @@ -104,6 +106,18 @@ + + + + + + + + + + + + diff --git a/BizHawk.Emulation/CPUs/ARM/ARM.CP.cs b/BizHawk.Emulation/CPUs/ARM/ARM.CP.cs new file mode 100644 index 0000000000..af027849f4 --- /dev/null +++ b/BizHawk.Emulation/CPUs/ARM/ARM.CP.cs @@ -0,0 +1,60 @@ +using System; + +namespace BizHawk.Emulation.CPUs.ARM +{ + + partial class ARM + { + public ARM.CP15 cp15; + + public uint cp15_MRC(uint opc1, uint t, uint crn, uint crm, uint opc2) + { + if (t == 15) unpredictable = true; + switch (crn) + { + case 13: + switch (opc2) + { + case 0: return cp15.FCSEIDR; + case 1: return cp15.CONTEXTIDR; + case 2: return cp15.TPIDRURW; + case 3: return cp15.TPIDRURO; + case 4: return cp15.TPIDRPRW; + } + break; + } + + //unhandled... + unpredictable = true; + return 0; + } + + public string cp15_Describe(uint opc1, uint t, uint crn, uint crm, uint opc2) + { + switch (crn) + { + case 13: + switch (opc2) + { + case 0: return "CP15.FCSEIDR: FCSE PID"; + case 1: return "CP15.CONTEXTIDR: Context ID"; + case 2: return "CP15.TPIDRURW: User RW Thread ID"; + case 3: return "CP15.TPIDRURW: User RO Thread ID"; + case 4: return "CP15.TPIDRURW: Priv. Only Thread ID"; + } + break; + } + + return "unknown"; + } + + public class CP15 + { + //c13 + public uint FCSEIDR; + public uint CONTEXTIDR; + public uint TPIDRURW, TPIDRURO, TPIDRPRW; + } + } + +} \ No newline at end of file diff --git a/BizHawk.Emulation/CPUs/ARM/ARM.Disassembly.cs b/BizHawk.Emulation/CPUs/ARM/ARM.Disassembly.cs new file mode 100644 index 0000000000..3c05c84489 --- /dev/null +++ b/BizHawk.Emulation/CPUs/ARM/ARM.Disassembly.cs @@ -0,0 +1,464 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk.Emulation.CPUs.ARM +{ + partial class ARM + { + public class DisassemblyOptions + { + //TODO - refactor these to NumberOptions or somesuch and have several of those + //0 = unpadded ; 1,2,4 = numbytes: -1 = no hex + public int SVC_hex; + public bool SVC_hexPrefix, SVC_literalPrefix, SVC_caps; + public bool SVC_showName = true; + + public void SetNstyle() + { + SVC_hex = 0; + SVC_hexPrefix = false; + SVC_literalPrefix = false; + SVC_caps = true; + hideZeroOffset = true; + //showExplicitAccumulateRegisters = false; + } + + public bool showoptadd = false; + + //fixes retarded debugger label format by adding #0x in front of it + public bool fix_nstyle_label = true; + + //TODO - subsume nstyle + + //show all registers when accumulating values, i.e. add r0, r0, #4 (instead of add r0, #4) + //TODO - obseleted by rd~rx approach + public bool showExplicitAccumulateRegisters = true; + + //hide offsets of #0 (set to true for more terseness with no real loss of information) + public bool hideZeroOffset = false; + + //compute PC relative labels for certain operations (like LDR) + //public bool computePCLabel = true; + + //consider: + //in RVCT many (all?) thumb instructions dont show {S} suffix in thumb mode, because it is sort of redundant (since they always apply in many thumb instructions) + //but this is gay. well, consider making it an option + } + + public class ProcessorOptions + { + public void SetNStyle() + { + Thumb_LSL_immediate_T1_0_is_MOV_register_T2 = false; + } + + public bool Thumb_LSL_immediate_T1_0_is_MOV_register_T2 = true; + } + + public DisassemblyOptions disopt = new DisassemblyOptions(); + public ProcessorOptions procopt = new ProcessorOptions(); + + bool DIS_Check(object[] args, int argnum, string str, string find) + { + if (args.Length <= argnum) return false; + int index = str.IndexOf(find); + if (index == -1) return false; + return true; + } + string disformat_num(object item, bool caps, bool literalprefix, bool hexprefix, int hex) + { + string hexformat; + switch (hex) + { + case -1: hexformat = "{0}"; break; + case 0: hexformat = "{0:x}"; break; + case 1: hexformat = "{0:x2}"; break; + case 2: hexformat = "{0:x4}"; break; + case 4: hexformat = "{0:x8}"; break; + default: throw new ArgumentException(); + } + string value = string.Format(hexformat, item); + if (caps) value = value.ToUpper(); + string ret = string.Format("{0}{1}{2}", literalprefix ? "#" : "", hexprefix ? "0x" : "", value); + return ret; + } + uint DIS(string opcode, string format, params object[] args) + { + //options: + //capital reg names + //capital mnemonic + //tab between mnemonic and arguments (always? sometimes) + //0 front-pad hex arguments + const bool capMnemonic = true; + + if (DIS_Check(args, 0, opcode, "/s0/")) opcode = opcode.Replace("/s0/", (bool)args[0] ? "s" : ""); + + if (_CurrentCond() == 14) + opcode = opcode.Replace("/c/", ""); + else opcode = opcode.Replace("/c/", CC_strings[_CurrentCond()]); + + if (capMnemonic) opcode = opcode.ToUpper(); + + string ret = string.Format(opcode + " " + format, args); + + if (DIS_Check(args, 0, ret, "/a0/")) ret = ret.Replace("/a0/", string.Format("{0:x}", args[0])); //address format + if (DIS_Check(args, 1, ret, "/a1/")) ret = ret.Replace("/a1/", string.Format("{0:x}", args[1])); + if (DIS_Check(args, 0, ret, "/r0/")) ret = ret.Replace("/r0/", Reg_names[(uint)args[0]].ToLower()); + if (DIS_Check(args, 1, ret, "/r1/")) ret = ret.Replace("/r1/", Reg_names[(uint)args[1]].ToLower()); + if (DIS_Check(args, 2, ret, "/r2/")) ret = ret.Replace("/r2/", Reg_names[(uint)args[2]].ToLower()); + if (DIS_Check(args, 0, ret, "/imm8_0/")) ret = ret.Replace("/imm8_0/", string.Format("#{0}", (uint)args[0])); + if (DIS_Check(args, 1, ret, "/imm8_1/")) ret = ret.Replace("/imm8_1/", string.Format("#{0}", (uint)args[1])); + if (DIS_Check(args, 1, ret, "/uimm32h_1/")) ret = ret.Replace("/uimm32h_1/", string.Format("#0x{0:x}", (uint)args[1])); + if (DIS_Check(args, 0, ret, "/label0/")) ret = ret.Replace("/label0/", string.Format("{0:x}", args[0])); + if (DIS_Check(args, 1, ret, "/label1/")) ret = ret.Replace("/label1/", string.Format("{0:x}", args[1])); + if (DIS_Check(args, 0, ret, "/svc0/")) ret = ret.Replace("/svc0/", disformat_num(args[0], disopt.SVC_caps, disopt.SVC_literalPrefix, disopt.SVC_hexPrefix, disopt.SVC_hex)); + if (DIS_Check(args, 0, ret, "/const0/")) ret = ret.Replace("/const0/", "#0x" + string.Format("{0:x}", args[0]).ToUpper()); + if (DIS_Check(args, 1, ret, "/const1/")) ret = ret.Replace("/const1/", "#0x" + string.Format("{0:x}", args[1]).ToUpper()); + if (DIS_Check(args, 2, ret, "/const2/")) ret = ret.Replace("/const2/", "#0x" + string.Format("{0:x}", args[2]).ToUpper()); + //TODO - run constN through the disformat scheme + ret = DIS_Reglist(ret, args); + disassembly = ret; + return 0; + } + + string DISNEW_optaddsub(object value) + { + return (bool)value ? (disopt.showoptadd ? "+" : "") : "-"; + } + + uint DISNEW(string opcode, string format, params object[] args) + { + bool hasComment = false; + + const bool capMnemonic = true; + + int index = 0; + int tagindex = -1; + int argindex = 0; + uint regReport = 0; + + //----- + //work on opcode + if (opcode.IndexOf("") != -1) + { + bool s = (bool)args[argindex++]; + opcode = opcode.Replace("", s ? "s" : ""); + } + + //TODO - look for cmaybe and get rid of them. + //alternatively, do a good job placing them and just always choose to put them there. + //well, we can do that as a separate polish pass later + bool cmaybe = (opcode.IndexOf("") != -1); + bool usec = true; + if (cmaybe) + usec = (bool)args[argindex++]; + string crepl = cmaybe ? "" : ""; + + if (usec) + { + if (_CurrentCond() == 14) + opcode = opcode.Replace(crepl, ""); + else opcode = opcode.Replace(crepl, CC_strings[_CurrentCond()]); + } + else opcode = opcode.Replace(crepl, ""); + //--------- + + string cpcomment = null; + while (index < format.Length) + { + if (tagindex != -1) + { + if (format[index] == '>') + { + int len = index - tagindex - 1; + string item = format.Substring(tagindex + 1, len).ToLower(); + + switch (item) + { + case "rt": + case "rm": + case "rn": + case "rd": + { + item = Reg_names[(uint)args[argindex++]].ToLower(); + break; + } + + case "rt!": + case "rm!": + case "rn!": + case "rd!": + case "sp!": + { + uint reg; + if (item == "sp!") reg = 13; + else reg = (uint)args[argindex++]; + item = Reg_names[reg].ToLower(); + regReport |= (uint)(1 << (int)reg); + break; + } + + case "{rd!~rm, }": + case "{rd!~rn, }": + { + uint rd = (uint)args[argindex++]; + uint rx = (uint)args[argindex++]; + if (disopt.showExplicitAccumulateRegisters || rd != rx) + item = string.Format("{0}, ", Reg_names[rd].ToLower()); + else item = ""; + break; + } + + case "fpscr": item = nstyle ? "fpscr" : "FPSCR"; break; + + case "const": item = string.Format("0x{0:x}", args[argindex++]); break; + case "imm": item = string.Format("0x{0:x}", args[argindex++]); break; + case "imm5": item = string.Format("0x{0:x}", args[argindex++]); break; + + case "{ ,#imm}": + { + uint temp = (uint)args[argindex++]; + if (temp == 0) item = ""; + else item = string.Format(",#0x{0:x}", temp); + break; + } + case "{,+/-#imm}": + { + uint temp = (uint)args[argindex + 1]; + if (temp == 0 && disopt.hideZeroOffset) item = ""; + else item = string.Format(",#{0}0x{1:x}", DISNEW_optaddsub(args[argindex]), temp); + argindex++; + break; + } + //case "labelaccess": //same as label but with [] around it + case "label": + if (disopt.fix_nstyle_label) + item = string.Format("#0x{0:x}", args[argindex++]); + else + item = string.Format("{0:x}", args[argindex++]); + break; + case "%08x": item = string.Format("0x{0:x8}", args[argindex++]); break; + case "optaddsub": + case "+/-": item = DISNEW_optaddsub((bool)args[argindex++]); break; + + case "{, shift}": + { + //TODO - consider whether it is necessary to pass in arguments (can they always be pulled from shift_n and shift_t? i think so + SRType sr = (SRType)args[argindex++]; + int shift_n = (int)args[argindex++]; + switch (sr) + { + case SRType.LSL: + if (shift_n == 0) + { + //special case for non-rotated things. theyre usually encoded this way + item = ""; + break; + } + item = ", LSL " + shift_n.ToString(); break; + case SRType.LSR: item = ", LSR " + shift_n.ToString(); break; + case SRType.ASR: item = ", ASR " + shift_n.ToString(); break; + case SRType.ROR: item = ", ROR " + shift_n.ToString(); break; + case SRType.RRX: item = ", RRX"; break; + } + break; + } + case "{, rotation}": + { + uint rotation = (uint)args[argindex++] * 8; + if (rotation == 0) item = ""; + else item = "ROR #" + rotation; + break; + } + + case "registers": + item = DISNEW_Reglist((uint)args[argindex++]); + break; + case "svc": + { + uint svc = (uint)args[argindex++]; + if (nstyle) opcode = opcode.Replace("XXX", "SWI"); + else opcode = opcode.Replace("XXX", "SVC"); + item = disformat_num(svc, disopt.SVC_caps, disopt.SVC_literalPrefix, disopt.SVC_hexPrefix, disopt.SVC_hex); + if (disopt.SVC_showName) { item += " ; " + sys.svc_name(svc); hasComment = true; } + break; + } + case "{wback!}": + if ((bool)args[argindex++]) + item = "!"; + else item = ""; + break; + + case "coproc": + item = "cp" + args[argindex++].ToString(); + break; + case "opc1": + case "opc2": + item = "#" + args[argindex++].ToString(); + break; + case "coproc_rt!": + { + uint reg = (uint)args[argindex++]; + if (reg == 15) + { + item = "APSR_nzcv"; + } + else + regReport |= (uint)(1 << (int)reg); + item = Reg_names[reg].ToLower(); + break; + } + case "crn": + case "crm": + item = "c" + args[argindex++].ToString(); + break; + case "{ ;cp_comment}": + { + item = ""; + cpcomment = args[argindex++] as string; + } + + break; + + default: item = "<" + item + ">"; break; + } + + format = format.Substring(0, tagindex) + item + format.Substring(tagindex + len + 2); + index = tagindex + item.Length - 1; + tagindex = -1; + } + } + else + if (format[index] == '<') + tagindex = index; + index++; + } + + if (capMnemonic) opcode = opcode.ToUpper(); + + disassembly = opcode + " " + format; + if (unpredictable) + { + disassembly = disassembly + " ; UNPREDICTABLE"; + hasComment = true; + } + + //report any relevant registers + bool hasRegReport = false; + for (int i = 0; i < 16; i++) + { + uint doit = regReport & 1; + regReport >>= 1; + if (doit == 1) + { + if (!hasComment) disassembly += " ; "; + if (hasRegReport) disassembly += ", "; + hasComment = true; + hasRegReport = true; + disassembly += string.Format("{0}={1:x8}", Reg_names[i].ToLower(), r[i]); + } + } + + if (!string.IsNullOrEmpty(cpcomment)) + { + disassembly += " ;" + cpcomment; + } + + return 0; + } + + string DIS_Reglist(string str, object[] args) + { + int index = str.IndexOf("/reglist/"); + if (index == -1) return str; + uint regs = (uint)args[0]; + StringBuilder sb = new StringBuilder(32); + sb.Append("{"); + //TODO - coalesce these to the range style!! + bool first = true; + for (int i = 0; i <= 15; i++) + { + if (_.BITN(i, regs) == 1) + { + if (!first) sb.Append(","); + sb.Append(Reg_names[i].ToLower()); + first = false; + } + } + sb.Append("}"); + return str.Replace("/reglist/", sb.ToString()); + } + + //TODO - unit test this + unsafe string DISNEW_Reglist(uint regs) + { + StringBuilder sb = new StringBuilder(32); + //TODO - coalesce these to the range style!! + const int firstname = 13; + bool first = true; + int range = -1; + int lastrange = -1; + int* ra = stackalloc int[8]; + int* rb = stackalloc int[8]; + for (int i = 0; i <= 15; i++) + { + bool bit = _.BITN(i, regs) != 0; + if (bit && i < firstname) + { + if (lastrange == -1) + { + lastrange = i; + } + else + { + + } + } + if (!bit || i >= firstname) + { + if (bit && i >= firstname) + { + range++; + ra[range] = i; + rb[range] = i; + } + if (lastrange != -1) + { + range++; + ra[range] = lastrange; + rb[range] = i - 1; + lastrange = -1; + } + } + } + + sb.Append("{"); + for (int i = 0; i <= range; i++) + { + int a = ra[i]; + int b = rb[i]; + if (!first) sb.Append(","); + if (a == b) sb.Append(Reg_names[a].ToLower()); + else sb.AppendFormat("{0}-{1}", Reg_names[a].ToLower(), Reg_names[b].ToLower()); + first = false; + } + sb.Append("}"); + + //sb.Append("{"); + //for (int i = 0; i <= 15; i++) + //{ + // if (_.BITN(i, regs) == 1) + // { + // if (!first) sb.Append(","); + // sb.Append(Reg_names[i].ToLower()); + // first = false; + // } + //} + //sb.Append("}"); + + return sb.ToString(); + } + + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/CPUs/ARM/ARM.Execute.cs b/BizHawk.Emulation/CPUs/ARM/ARM.Execute.cs new file mode 100644 index 0000000000..4eb87c4c3e --- /dev/null +++ b/BizHawk.Emulation/CPUs/ARM/ARM.Execute.cs @@ -0,0 +1,147 @@ +using System; +using System.Text; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.CPUs.ARM +{ + + partial class ARM + { + public uint Execute() + { + if (APSR.T == 1) return ExecuteThumb(); + else return ExecuteArm(); + } + + enum CC + { + EQ = 0, + NE = 1, + CS = 2, HS = 2, + CC = 3, LO = 3, + MI = 4, + PL = 5, + VS = 6, + VC = 7, + HI = 8, + LS = 9, + GE = 10, + LT = 11, + GT = 12, + LE = 13, + AL = 14, + } + //return (CPSR.N?0x80000000:0)|(CPSR.Z?0x40000000:0)|(CPSR.C?0x20000000:0)|(CPSR.V?0x10000000:0); + + readonly string[] CC_strings = { "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL", "XX" }; + readonly string[] Reg_names = { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12", "SP", "LR", "PC" }; + + uint CONDITION(uint i) { return i >> 28; } + int SIGNEXTEND_24(uint x) { return (((int)x << 8) >> 8); } + + //an instruction sets this flag when the results are unpredictable but will be executed anyway + public bool unpredictable; + public uint cycles; + uint _currentCondVal; + uint _CurrentCond() + { + //TODO - calculate CurrentCond from from A8.3.1 + return _currentCondVal; + } + bool _ConditionPassed() + { + uint cond = _CurrentCond(); + bool result = false; + switch (cond & _.b1110) + { + case _.b0000: result = APSR.Z == 1; break; + case _.b0010: result = APSR.C == 1; break; + case _.b0100: result = APSR.N == 1; break; + case _.b0110: result = APSR.V == 1; break; + case _.b1000: result = APSR.C == 1 && APSR.Z == 0; break; + case _.b1010: result = APSR.N == APSR.V; break; + case _.b1100: result = (APSR.N == APSR.V) && APSR.Z == 0; break; + case _.b1110: result = true; break; + } + if ((cond & 1) == 1 && cond != _.b1111) + result = !result; + return result; + } + + + bool CHK(uint value, int mask, int test) + { + return (value & mask) == test; + } + + uint Reg8(uint pos) + { + return (uint)((instruction >> (int)pos) & 7); + } + + uint Reg16(uint pos) + { + return (uint)((instruction >> (int)pos) & 0xF); + } + + uint Execute_Undefined() + { + if (disassemble) disassembly = "undefined"; + return 0; + } + + uint Execute_Unhandled(string descr) + { + if (disassemble) disassembly = descr + " unhandled"; + return 1; + } + + uint Disassembly_Unhandled(string descr) + { + disassembly = "disasm unhandled: " + descr; + return 1; + } + + uint Disassemble_LDR_STR_immediate(string opcode, uint t, uint imm32, uint n, bool index, bool add, bool wback) + { + bool offset = index == true && wback == false; + bool preindexed = index == true && wback == true; + bool postindex = index == false && wback == true; + + string rt = ""; + opcode += ""; + //we may want conditional logic here to control whether various registers are displayed (we used to have it, but i changed my mind) + if (offset) + return DISNEW(opcode, rt + ", [<{,+/-#imm}>]", t, n, add, imm32); + else if (preindexed) + return DISNEW(opcode, rt + ", [, <+/->]!", t, n, add, imm32); + else if (postindex) + return DISNEW(opcode, rt + ", [], <+/->", t, n, add, imm32); + else throw new InvalidOperationException(); + } + + uint Disassemble_LDR_STR_register(string opcode, uint t, uint n, uint m, SRType shift_t, int shift_n, bool index, bool add, bool wback) + { + bool offset = index == true && wback == false; + bool preindexed = index == true && wback == true; + bool postindex = index == false && wback == true; + + string rt = ""; + opcode += ""; + //we may want conditional logic here to control whether various registers are displayed (we used to have it, but i changed my mind) + if (offset) + return DISNEW(opcode, rt + ", [, <+/-><{, shift}>]", t, n, add, m, shift_t, shift_n); + else if (preindexed) + return DISNEW(opcode, rt + ", [, <+/-><{, shift}>]!", t, n, add, m, shift_t, shift_n); + else if (postindex) + return DISNEW(opcode, rt + ", [], <+/-><{, shift}>", t, n, add, m, shift_t, shift_n); + else throw new InvalidOperationException(); + } + + void UnalignedAccess(uint addr) + { + Console.WriteLine("Warning! unaligned access at {0:x8}", addr); + } + } + +} \ No newline at end of file diff --git a/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.Unconditional.cs b/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.Unconditional.cs new file mode 100644 index 0000000000..898f10112a --- /dev/null +++ b/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.Unconditional.cs @@ -0,0 +1,61 @@ +namespace BizHawk.Emulation.CPUs.ARM +{ + partial class ARM + { + uint ExecuteArm_Unconditional() + { + //A5.7 + + uint op1 = (instruction & 0xFF00000) >> 20; + uint op = (instruction & 0x10) >> 4; + + //todo: misc instructions, memory hints, and advanced SIMD instructions on page A5-31 + if (CHK(op1, _.b10000000, _.b00000000)) return ExecuteArm_Unconditional_Misc(); + if (CHK(op1, _.b11100101, _.b10000100)) return Execute_SRS_A1(); //v6 + if (CHK(op1, _.b11100101, _.b10000001)) return Execute_RFE_A1(); //v6 + if (CHK(op1, _.b11100000, _.b10100000)) return Execute_BL_BLX_immediate_A2(); //v5 + if (CHK(op1, _.b11111011, _.b11000011)) return ExecuteArm_LDC_LDC2_immediate(); //v5 + if (CHK(op1, _.b11111001, _.b11000011)) return ExecuteArm_LDC_LDC2_literal(1); //v5 + if (CHK(op1, _.b11110001, _.b11010001)) return ExecuteArm_LDC_LDC2_literal(2); //v5 + if (CHK(op1, _.b11111011, _.b11000010)) return ExecuteArm_STC_STC2(1); //v5 + if (CHK(op1, _.b11111001, _.b11001000)) return ExecuteArm_STC_STC2(2); //v5 + if (CHK(op1, _.b11110001, _.b11010000)) return ExecuteArm_STC_STC2(3); //v5 + if (op1 == _.b11000100) return ExecuteArm_MCRR_MCRR2(); //v6 + if (op1 == _.b11000101) return ExecuteArm_MRRC_MRRC2(); //v6 + if (CHK(op1, _.b11110000, _.b11100000) && op == 0) return ExecuteArm_CDP_CDP2(); //v5 + if (CHK(op1, _.b11110001, _.b11100000) && op == 1) return ExecuteArm_MCR_MCR2(); //v5 + if (CHK(op1, _.b11110001, _.b11100001) && op == 1) return ExecuteArm_MRC_MRC2(); //v5 + + return Execute_Unhandled("ExecuteArm_Unconditional"); + } + + uint ExecuteArm_Unconditional_Misc() { return Execute_Unhandled("ExecuteArm_Unconditional_Misc"); } + uint Execute_SRS_A1() { return Execute_Unhandled("ExecuteArm_SRS_A1"); } + uint Execute_RFE_A1() { return Execute_Unhandled("ExecuteArm_RFE_A1"); } + uint Execute_BL_BLX_immediate_A2() + { + //A8.6.23 + uint imm24 = instruction & 0xFFFFFF; + uint H = _.BIT24(instruction); + int imm32 = SIGNEXTEND_24((imm24 << 2) | (H << 1)); + return ExecuteCore_BL_BLX_immediate(Encoding.A2, EInstrSet.THUMB, imm32, true); + } + + + + uint ExecuteArm_LDC_LDC2_immediate() + { + uint Rn = instruction & 0xF0000; + if (Rn == 0xF) { } //special handling for 0xF vs not 0xF + return Execute_Unhandled("ExecuteArm_LDC_LDC2_immediate"); + } + uint ExecuteArm_LDC_LDC2_literal(int form) { return Execute_Unhandled("ExecuteArm_LDC_LDC2_literal"); } + uint ExecuteArm_STC_STC2(int form) { return Execute_Unhandled("ExecuteArm_STC_STC2"); } + uint ExecuteArm_MCRR_MCRR2() { return Execute_Unhandled("ExecuteArm_MCRR_MCRR2"); } + uint ExecuteArm_MRRC_MRRC2() { return Execute_Unhandled("ExecuteArm_MRRC_MRRC2"); } + uint ExecuteArm_CDP_CDP2() { return Execute_Unhandled("ExecuteArm_CDP_CDP2"); } + uint ExecuteArm_MCR_MCR2() { return Execute_Unhandled("ExecuteArm_MCR_MCR2"); } + uint ExecuteArm_MRC_MRC2() { return Execute_Unhandled("ExecuteArm_MRC_MRC2"); } + } + +} \ No newline at end of file diff --git a/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.cs b/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.cs new file mode 100644 index 0000000000..bd859b15d9 --- /dev/null +++ b/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteArm.cs @@ -0,0 +1,785 @@ +using System; +using System.Diagnostics; +using System.Text; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.CPUs.ARM +{ + + partial class ARM + { + uint ExecuteArm() + { + _currentCondVal = CONDITION(instruction); + if (_currentCondVal == 0xF) return ExecuteArm_Unconditional(); + bool pass = _ConditionPassed() || disassemble; + if (pass) + { + uint op1 = (instruction & 0x0E000000) >> 25; + uint op = (instruction & 0x10) >> 4; + switch (op1) + { + case _.b000: + case _.b001: + return ExecuteArm_DataProcessing(); + case _.b010: + return ExecuteArm_LoadStore(); + case _.b011: + if (op == 0) return ExecuteArm_LoadStore(); + else return ExecuteArm_Media(); + case _.b100: + case _.b101: + return ExecuteArm_BranchAndTransfer(); + case _.b110: + case _.b111: + return ExecuteArm_SVCAndCP(); + default: + throw new InvalidOperationException(); + } + } + else + return 1; + } + + + Decoder decoder_ExecuteArm_LoadStore = new Decoder(); + uint ExecuteArm_LoadStore() + { + //A5.3 + //A5-19 + + decoder_ExecuteArm_LoadStore.Ensure(() => decoder_ExecuteArm_LoadStore + .d("A", 1).d("op1", 5).d("B", 1).d("Rn", 4) + .r("A==0 && op1 == #xx0x0 && op1 != #0x010", () => Execute_STR_immediate_A1()) + .r("A==1 && op1 == #xx0x0 && op1 != #0x010 && B==0", () => Execute_Unhandled("STR (register) on page A8-386")) + .r("A==0 && op1 == #0x010", () => Execute_Unhandled("STRT on page A8-416")) + .r("A==1 && op1 == #0x010 && B==0", () => Execute_Unhandled("STRT on page A8-416")) + .r("A==0 && op1 == #xx0x1 && op1 != #0x011 && Rn != #1111", () => Execute_LDR_immediate_arm_A1()) + .r("A==0 && op1 == #xx0x1 && op1 != #0x011 && Rn == #1111", () => ExecuteArm_LDR_literal_A1()) + .r("A==1 && op1 == #xx0x1 && A != #0x011 && B==0", () => Execute_Unhandled("LDR (register) on page A8-124")) + .r("A==0 && op1 == #0x011", () => Execute_Unhandled("LDRT on page A8-176")) + .r("A==1 && op1 == #0x011 && B==0", () => Execute_Unhandled("LDRT on page A8-176")) + .r("A==0 && op1 == #xx1x0 && op1 != #0x110", () => Execute_Unhandled("STRB (immediate, ARM) on page A8-390")) + .r("A==1 && op1 == #xx1x0 && op1 != #0x110 && B==0", () => Execute_Unhandled("STRB (register) on page A8-392")) + .r("A==0 && op1 == #0x110", () => Execute_Unhandled("STRBT on page A8-394")) + .r("A==1 && op1 == #0x110 && B==0", () => Execute_Unhandled("STRBT on page A8-394")) + .r("A==0 && op1 == #xx1x1 && op1 != #0x111 && Rn != #1111", () => Execute_Unhandled("LDRB (immediate, ARM) on page A8-128")) + .r("A==0 && op1 == #xx1x1 && op1 != #0x111 && Rn == #1111", () => Execute_Unhandled("LDRB (literal) on page A8-130")) + .r("A==1 && op1 == #xx1x1 && op1 != #0x111 && B==0", () => Execute_Unhandled("LDRB (register) on page A8-132")) + .r("A==0 && op1 == #0x111", () => Execute_Unhandled("LDRBT on page A8-134")) + .r("A==1 && op1 == #0x111 && B==0", () => Execute_Unhandled("LDRBT on page A8-134")) + ); + + uint A = _.BIT25(instruction); + uint op1 = (instruction >> 20) & 0x1F; + uint B = _.BIT4(instruction); + uint Rn = Reg16(16); + decoder_ExecuteArm_LoadStore.Evaluate(A, op1, B, Rn); + + return cycles; + } + + uint Execute_LDR_immediate_arm_A1() + { + //A8.6.58 LDR (immediate, ARM) + Bit P = _.BIT24(instruction); + Bit U = _.BIT23(instruction); + Bit W = _.BIT21(instruction); + uint n = Reg16(16); + uint t = Reg16(12); + uint imm12 = instruction & 0xFFF; + if (n == _.b1111) throw new NotImplementedException("see LDR (literal"); + if (P == 0 && W == 1) throw new NotImplementedException("see LDRT"); + if (n == _.b1101 && P == 0 && U == 1 && W == 0 && imm12 == _.b000000000100) + return Execute_POP_A2(); + uint imm32 = _ZeroExtend_32(imm12); + bool index = (P == 1); + bool add = (U == 1); + bool wback = (P == 0) || (W == 1); + if (wback && n == t) unpredictable = true; + + return ExecuteCore_LDR_immediate_arm(Encoding.A1, t, imm32, n, index, add, wback); + } + + uint Execute_POP_A2() + { + //A8.6.122 POP + uint t = Reg16(12); + uint regs = (uint)(1 << (int)t); + if (t == 13) unpredictable = true; + const bool UnalignedAllowed = true; + return ExecuteCore_POP(Encoding.A2, regs, UnalignedAllowed); + } + + uint Execute_STR_immediate_A1() + { + //A8.6.194 + Bit P = _.BIT24(instruction); + Bit U = _.BIT23(instruction); + Bit W = _.BIT21(instruction); + uint n = Reg16(16); + uint t = Reg16(12); + uint imm32 = instruction & 0xFFF; + if (P == 0 && W == 1) throw new NotImplementedException("see STRT"); + if (n == _.b1101 && P == 1 && U == 0 && W == 1 && imm32 == _.b000000000100) + return Execute_PUSH_A2(); + bool index = (P == 1); + bool add = (U == 1); + bool wback = (P == 0) || (W == 1); + if (wback && n == 16 || n == t) unpredictable = true; + + return ExecuteCore_STR_immediate_arm(Encoding.A1, P, U, W, n, t, imm32, index, add, wback); + } + + uint Execute_PUSH_A2() + { + //A8.6.123 + uint t = Reg8(12); + bool UnalignedAllowed = true; + uint registers = (uint)(1 << (int)t); + if (t == 13) _FlagUnpredictable(); + return ExecuteCore_PUSH(Encoding.A2, registers, UnalignedAllowed); + } + + uint ExecuteArm_LDR_literal_A1() + { + //A8.6.59 + //A8-122 + uint t = Reg16(12); + uint imm32 = instruction & 0xFFF; + uint U = _.BIT23(instruction); + bool add = (U == 1); + + return ExecuteCore_LDR_literal(Encoding.A1, t, imm32, add); + } + + + uint ExecuteArm_DataProcessing() + { + uint op = _.BIT25(instruction); + uint op1 = (instruction >> 20) & 0x1F; + uint op2 = (instruction >> 4) & 0xF; + switch (op) + { + case 0: + if (!CHK(op1, _.b11001, _.b10000)) + if (CHK(op2, _.b0001, _.b0000)) return ExecuteArm_DataProcessing_Register(); + else if (CHK(op2, _.b1001, _.b0001)) return Execute_Unhandled("data-processing (register-shifted register) on page A5-7"); + if (CHK(op1, _.b11001, _.b10000)) + if (CHK(op2, _.b1000, _.b0000)) return ExecuteArm_DataProcessing_Misc(); + else if (CHK(op2, _.b1001, _.b1000)) return Execute_Unhandled("halfword multiply and multiply-accumulate on page A5-13"); + if (CHK(op1, _.b10000, _.b00000) && op2 == _.b1001) return Execute_Unhandled("multiply and multiply-accumulate on page A5-12"); + if (CHK(op1, _.b10000, _.b10000) && op2 == _.b1001) return ExecuteArm_SynchronizationPrimitives(); + if (!CHK(op1, _.b10010, _.b00010)) + if (op2 == _.b1011) return Execute_Unhandled("extra load/store instructions on page A5-14"); + else if (CHK(op2, _.b1011, _.b1101)) return Execute_Unhandled("extra load/store instructions on page A5-14"); + if (CHK(op1, _.b10010, _.b00010)) + if (op2 == _.b1011) return Execute_Unhandled("extra load/store instructions (unprivileged) on page A5-15"); + else if (CHK(op2, _.b1011, _.b1101)) return Execute_Unhandled("extra load/store instructions (unprivileged) on page A5-15"); + throw new InvalidOperationException("unexpected decoder fail"); + case 1: + if (!CHK(op1, _.b11001, _.b10000)) + return ExecuteArm_DataProcessing_Immediate(); + if (op1 == _.b10000) return Execute_Unhandled("16-bit immediate load (MOV (immediate on page A8-193) //v6T2"); + if (op1 == _.b10100) return Execute_Unhandled("high halfword 16-bit immediate load (MOVT on page A8-200) //v6T2"); + if (CHK(op1, _.b11011, _.b10010)) return Execute_Unhandled("MSR (immediate), and hints on page A5-17"); + throw new InvalidOperationException("unexpected decoder fail"); + + default: + throw new InvalidOperationException("totally impossible decoder fail"); + } + } + + uint ExecuteArm_SynchronizationPrimitives() + { + uint op = (instruction >> 20) & 0xF; + switch (op) + { + case _.b0000: + case _.b0100: return Execute_Unhandled("ExecuteArm_SWP_SWPB();"); + case _.b1000: return Execute_STREX_A1(); + case _.b1001: return Execute_LDREX_A1(); + case _.b1010: return Execute_Unhandled("ExecuteArm_STREXD();"); + case _.b1011: return Execute_Unhandled("ExecuteArm_LDREXD();"); + case _.b1100: return Execute_Unhandled("ExecuteArm_STREXB();"); + case _.b1101: return Execute_Unhandled("ExecuteArm_LDREXB();"); + case _.b1110: return Execute_Unhandled("ExecuteArm_STREXH();"); + case _.b1111: return Execute_Unhandled("ExecuteArm_LDREXH();"); + default: throw new InvalidOperationException("decoder fail"); + } + } + + uint Execute_STREX_A1() + { + //A8.6.202 STREX + uint n = Reg16(16); + uint d = Reg16(12); + uint t = Reg16(0); + const uint imm32 = 0; + if (d == 15 || t == 15 || n == 15) unpredictable = true; + if (d == n || d == t) unpredictable = true; + return ExecuteCore_STREX(Encoding.A1, d, n, t, imm32); + } + + uint Execute_LDREX_A1() + { + //A8.6.69 LDREX + uint n = Reg16(16); + uint t = Reg16(12); + uint imm32 = 0; + if (t == 15 || n == 15) unpredictable = true; + return ExecuteCore_LDREX(Encoding.A1, n, t, imm32); + } + + uint ExecuteArm_DataProcessing_Register() + { + //A5.2.1 + uint op1 = (instruction >> 20) & 0x1F; + uint op2 = (instruction >> 7) & 0x1F; + uint op3 = (instruction >> 5) & 3; + + switch (op1) + { + case _.b00000: + case _.b00001: return Execute_Unhandled("arm and reg"); + case _.b00010: + case _.b00011: return Execute_Unhandled("arm eor reg"); + case _.b00100: + case _.b00101: return Execute_Unhandled("arm sub reg"); + case _.b00110: + case _.b00111: return Execute_Unhandled("arm rsb reg"); + case _.b01000: + case _.b01001: return Execute_ADD_register_A1(); + case _.b01010: + case _.b01011: return Execute_Unhandled("arm adc reg"); + case _.b01100: + case _.b01101: return Execute_Unhandled("arm sbc reg"); + case _.b01110: + case _.b01111: return Execute_Unhandled("arm rsc reg"); + case _.b10001: return Execute_Unhandled("arm tst reg"); + case _.b10011: return Execute_Unhandled("arm teq reg"); + case _.b10101: return Execute_CMP_register_A1(); + case _.b10111: return Execute_Unhandled("arm cmn reg"); + case _.b11000: + case _.b11001: return Execute_Unhandled("arm orr reg"); + case _.b11010: + case _.b11011: + if (op2 == _.b00000 && op3 == _.b00) return Execute_MOV_register_A1(); + if (op2 != _.b00000 && op3 == _.b00) return Execute_LSL_immediate_A1(); + if (op3 == _.b01) return Execute_LSR_immediate_A1(); + if (op3 == _.b10) return Execute_Unhandled("arm asr imm"); + if (op2 == _.b00000 && op3 == _.b11) return Execute_Unhandled("arm rrx"); + if (op2 != _.b00000 && op3 == _.b11) return Execute_Unhandled("arm ror imm"); + throw new InvalidOperationException("decode fail"); + case _.b11100: + case _.b11101: return Execute_Unhandled("arm bic reg"); + case _.b11110: + case _.b11111: return Execute_Unhandled("arm mvn reg"); + default: + throw new InvalidOperationException("decode fail"); + } + } + + uint Execute_LSR_immediate_A1() + { + //A8.6.90 + uint d = Reg16(12); + uint m = Reg16(0); + Bit S = _.BIT20(instruction); + bool setflags = (S == 1); + uint imm5 = (instruction >> 7) & 0x1F; + _DecodeImmShift(_.b01, imm5); + return ExecuteCore_LSR_immediate(Encoding.A1, d, m, setflags, shift_n); + } + + uint Execute_LSL_immediate_A1() + { + //A8.6.87 + Bit S = _.BIT20(instruction); + uint d = Reg16(12); + uint imm5 = (instruction >> 7) & 0x1F; + uint m = Reg16(0); + Debug.Assert(imm5 != _.b00000); //should have been prevented by decoder + bool setflags = (S == 1); + _DecodeImmShift(_.b00, imm5); + return ExecuteCore_LSL_immediate(Encoding.A1, d, m, setflags, shift_n); + } + + uint Execute_MOV_register_A1() + { + //A8.6.97 + Bit S = _.BIT20(instruction); + uint d = Reg16(12); + uint m = Reg16(0); + + if (d == _.b1111 && S == 1) throw new NotSupportedException("SEE SUBS PC, LR and related instructions"); + bool setflags = (S == 1); + return ExecuteCore_MOV_register(Encoding.A1, d, m, setflags); + } + + uint Execute_ADD_register_A1() + { + //A8.6.6 ADD (register) + uint m = Reg16(0); + uint type = (instruction >> 4) & 3; + uint imm5 = (instruction >> 7) & 0x1F; + uint d = Reg16(12); + uint n = Reg16(16); + Bit s = _.BIT20(instruction); + bool setflags = (s == 1); + if (d == _.b1111 && s == 1) { throw new InvalidOperationException("see SUBS PC, LR and related instructions;"); } + if (n == _.b1101) { throw new InvalidOperationException("see ADD (SP plus register);"); } + _DecodeImmShift(type, imm5); + + return ExecuteCore_ADD_register(Encoding.A1, m, d, n, setflags, shift_t, shift_n); + } + + uint Execute_CMP_register_A1() + { + //A8.6.36 + //A8-82 + uint n = Reg16(16); + uint imm5 = (instruction >> 7) & 0x1F; + uint m = Reg16(0); + uint type = (instruction >> 5) & 3; + _DecodeImmShift(type, imm5); + + if (disassemble) + return DISNEW("CMP", ",<,shift>", n, m, shift_t, shift_n); + + uint shifted = _Shift(r[m], shift_t, shift_n, APSR.C); + _AddWithCarry32(r[n], ~shifted, 1); + APSR.N = _.BIT31(alu_result_32); + APSR.Z = alu_result_32 == 0; + APSR.C = alu_carry_out; + APSR.V = alu_overflow; + + return 0; + } + + uint ExecuteArm_DataProcessing_Immediate() + { + //A5.2.3 + uint op = (instruction >> 20) & 0x1F; + uint Rn = Reg16(16); + switch (op) + { + case _.b00000: + case _.b00001: return Execute_Unhandled("arm and imm"); + case _.b00010: + case _.b00011: return Execute_Unhandled("arm eor imm"); + case _.b00100: + case _.b00101: + if (Rn != _.b1111) return Execute_SUB_immediate_arm_A1(); + else return Execute_Unhandled("arm adr"); + case _.b00110: + case _.b00111: return Execute_Unhandled("arm rsb imm"); + case _.b01000: + case _.b01001: + if (Rn != _.b1111) return Execute_ADD_immedate_arm_A1(); + else return Execute_Unhandled("arm adr"); + case _.b01010: + case _.b01011: return Execute_Unhandled("arm adc imm"); + case _.b01100: + case _.b01101: return Execute_Unhandled("arm sbc imm"); + case _.b01110: + case _.b01111: return Execute_Unhandled("arm rsc imm"); + case _.b10000: + case _.b10010: + case _.b10100: + case _.b10110: return ExecuteArm_DataProcessing_Misc_Imm(); + case _.b10001: return Execute_Unhandled("arm tst imm"); + case _.b10011: return Execute_Unhandled("arm teq imm"); + case _.b10101: return Execute_CMP_immediate_A1(); + case _.b10111: return Execute_Unhandled("arm cmn imm"); + case _.b11000: + case _.b11001: return Execute_ORR_immediate_A1(); + case _.b11010: + case _.b11011: return Execute_MOV_immediate_A1(); + case _.b11100: + case _.b11101: return Execute_Unhandled("arm bic imm"); + case _.b11110: + case _.b11111: return Execute_Unhandled("arm mvn imm"); + default: throw new InvalidOperationException("decoder fail"); + } + } + + uint Execute_SUB_immediate_arm_A1() + { + //A8.6.212 + Bit S = _.BIT20(instruction); + uint n = Reg16(16); + uint d = Reg16(12); + uint imm12 = instruction & 0xFFF; + + if (n == _.b1111 && S == 0) throw new NotImplementedException("SEE ADR"); + if (n == _.b1101) throw new NotImplementedException("SEE SUB (SP minus immediate"); + if (n == _.b1111 && S == 1) throw new NotImplementedException("SEE SUBS PC, LR and related instructions"); + bool setflags = (S == 1); + uint imm32 = _ARMExpandImm(imm12); + return ExecuteCore_SUB_immediate_arm(Encoding.A1, setflags, n, d, imm32); + } + + uint Execute_ADD_immedate_arm_A1() + { + Bit S = _.BIT20(instruction); + uint n = Reg16(16); + uint d = Reg16(12); + uint imm12 = instruction & 0xFFF; + + if (n == _.b1111 && S == 0) { throw new NotImplementedException("SEE ADR"); } + if (n == _.b1101) return Execute_ADD_SP_plus_immediate_A1(); + if (d == _.b1111 && S == 1) throw new NotImplementedException("SEE SUBS PC, LR and related instructions"); + bool setflags = (S == 1); + uint imm32 = _ARMExpandImm(imm12); + + return ExecuteCore_ADD_immediate_arm(Encoding.A1, setflags, n, d, imm32); + } + + uint Execute_ADD_SP_plus_immediate_A1() + { + uint d = Reg16(12); + Bit S = _.BIT20(instruction); + uint imm12 = instruction & 0xFFF; + if (d == _.b1111 && S == 1) throw new NotImplementedException("SEE SUBS PC, LR and related instructions"); + bool setflags = (S == 1); + uint imm32 = _ARMExpandImm(imm12); + + return ExecuteCore_ADD_SP_plus_immedate(Encoding.A1, d, setflags, imm32); + } + + uint Execute_CMP_immediate_A1() + { + //A8.6.35 + uint Rn = Reg16(16); + uint imm12 = instruction & 0xFFF; + uint imm32 = _ARMExpandImm(imm12); + return ExecuteCore_CMP_immediate(Encoding.A1, Rn, imm32); + } + + uint Execute_ORR_immediate_A1() + { + //A8.6.113 + Bit S = _.BIT20(instruction); + uint n = Reg16(16); + uint d = Reg16(12); + uint imm12 = instruction & 0xFFF; + Debug.Assert(!(d == _.b1111 && S == 1), "SEE SUBS PC, LR and related instructions"); + bool setflags = (S == 1); + uint imm32; + Bit carry; + _ARMExpandImm_C(imm12, APSR.C, out imm32, out carry); + return ExecuteCore_ORR_immediate(Encoding.A1, n, d, setflags, imm32, carry); + } + + uint Execute_MOV_immediate_A1() + { + //A8.6.96 + uint Rd = Reg16(12); + uint S = _.BIT20(instruction); + uint imm12 = instruction & 0xFFF; + if (Rd == _.b1111 && S == 1) + throw new InvalidOperationException("see subs pc, lr and related instructions"); + bool setflags = (S == 1); + uint imm32; + Bit carry; + _ARMExpandImm_C(imm12, APSR.C, out imm32, out carry); + return ExecuteCore_MOV_immediate(Encoding.A1, Rd, setflags, imm32, carry); + } + + uint ExecuteArm_DataProcessing_Misc_Imm() + { + //A5-4 + //TODO + return Execute_Unhandled("ExecuteArm_DataProcessing_Misc_Imm"); + } + + uint ExecuteArm_DataProcessing_Misc() + { + //A5.2.12 + uint op = (instruction >> 21) & 0x3; + uint op1 = (instruction >> 16) & 0xF; + uint op2 = (instruction >> 4) & 0x7; + + switch (op2) + { + case _.b000: + switch (op) + { + case _.b00: + case _.b10: + return Execute_Unhandled("MRS"); + case _.b01: + switch (op1) + { + case _.b0000: + case _.b0100: + case _.b1000: + case _.b1100: return Execute_Unhandled("MSR (register) application level"); + case _.b0001: + case _.b0101: + case _.b1001: + case _.b1101: + case _.b0010: + case _.b0011: + case _.b0110: + case _.b0111: + case _.b1010: + case _.b1011: + case _.b1110: + case _.b1111: + return Execute_Unhandled("MSR (register) system level"); + default: + throw new InvalidOperationException("decoder fail"); + } + case _.b11: + return Execute_Unhandled("MSR (register) system level"); + default: + throw new InvalidOperationException("decoder fail"); + } + case _.b001: + switch (op) + { + case _.b01: return ExecuteArm_BX_A1(); + case _.b11: return Execute_Unhandled("ExecuteArm_CLZ"); + default: + return Execute_Undefined(); + } + case _.b010: + if (op == _.b01) return Execute_Unhandled("BXJ"); + else return Execute_Undefined(); + case _.b011: + if (op == _.b01) return Execute_Unhandled("BLX (register) on page A8-60"); + else return Execute_Undefined(); + case _.b100: return Execute_Undefined(); + case _.b101: return Execute_Unhandled("saturating addition and subtraction on page A5-13"); + case _.b110: return Execute_Undefined(); + case _.b111: + switch (op) + { + case _.b01: return Execute_Unhandled("BKPT on page A8-56"); + case _.b11: return Execute_Unhandled("SMC/SMI on page B6-18 //sec.ext"); + default: return Execute_Undefined(); + } + default: + throw new InvalidOperationException("decoder fail"); + } //switch(op2) + } + + uint ExecuteArm_BX_A1() + { + //A8-62 + //A8.6.25 + uint Rm = Reg16(0); + if (disassemble) + return DIS("BX/c/", "/r0/", Rm); + uint m = r[Rm]; + _BXWritePC(m); + return 1; + } + + uint ExecuteArm_Media() { return Execute_Unhandled("ExecuteArm_Media"); } + + Decoder decoder_ExecuteArm_BranchAndTransfer = new Decoder(); + uint ExecuteArm_BranchAndTransfer() + { + decoder_ExecuteArm_BranchAndTransfer.Ensure(() => decoder_ExecuteArm_BranchAndTransfer + .d("op", 6).d("R", 1).d("Rn", 4) + .r("op == #0000x0", () => Execute_Unhandled("STMDA/STMED on page A8-376")) + .r("op == #0000x1", () => Execute_Unhandled("LDMDA/LDMFA on page A8-112")) + .r("op == #0010x0", () => Execute_STM_STMIA_STMEA_A1()) + .r("op == #001001", () => Execute_Unhandled("LDM/LDMIA/LDMFD on page A8-110")) + .r("op == #001011 && Rn != #1101", () => Execute_Unhandled("LDM/LDMIA/LDMFD on page A8-110")) + .r("op == #001011 && Rn == #1101", () => Execute_POP_A1()) + .r("op == #010000", () => Execute_Unhandled("STMDB/STMFD on page A8-378")) + .r("op == #010010 && Rn != #1101", () => Execute_Unhandled("STMDB/STMFD on page A8-378")) + .r("op == #010010 && Rn == #1101", () => Execute_PUSH_A1()) + .r("op == #0100x1", () => Execute_Unhandled("LDMDB/LDMEA on page A8-114")) + .r("op == #0110x0", () => Execute_Unhandled("STMIB/STMFA on page A8-380")) + .r("op == #0110x1", () => Execute_Unhandled("LDMIB/LDMED on page A8-116")) + .r("op == #0xx1x0", () => Execute_Unhandled("STM (user registers) on page B6-22")) + .r("op == #0xx1x1 && R==0", () => Execute_Unhandled("LDM (user registers on page B6-5")) + .r("op == #0xx1x1 && R==1", () => Execute_Unhandled("LDM (exception return) on page B6-5")) + .r("op == #10xxxx", () => Execute_B_A1()) + .r("op == #11xxxx", () => Execute_BL_A1()) + ); + + uint op = (instruction >> 20) & 0x3F; + uint Rn = Reg16(16); + uint R = _.BIT15(instruction); + + decoder_ExecuteArm_BranchAndTransfer.Evaluate(op, R, Rn); + + return 1; + } + + + uint Execute_POP_A1() + { + uint register_list = instruction & 0xFFFF; + if (_.BitCount(register_list) < 2) return Execute_LDM_LDMIA_LDMFD_A1(); + bool UnalignedAllowed = false; + return ExecuteCore_POP(Encoding.A1, register_list, UnalignedAllowed); + } + + uint Execute_PUSH_A1() + { + uint register_list = instruction & 0xFFFF; + if (_.BitCount(register_list) < 2) return Execute_STMDB_STMFD_A1(); + bool UnalignedAllowed = false; + return ExecuteCore_PUSH(Encoding.A1, register_list, UnalignedAllowed); + } + + uint Execute_LDM_LDMIA_LDMFD_A1() + { + //A8.6.53 LDM/LDMIA/LDMFD + Bit W = _.BIT21(instruction); + uint n = Reg16(16); + uint register_list = instruction & 0xFFFF; + bool wback = (W == 1); + if (n == 15 || _.BitCount(register_list) < 1) unpredictable = true; + if (wback && _.BITN((int)n, register_list) == 1 && _ArchVersion() >= 7) unpredictable = true; + return ExecuteCore_LDM_LDMIA_LDMFD(Encoding.A1, wback, n, register_list); + } + + + uint Execute_STM_STMIA_STMEA_A1() + { + //A8.6.189 STM/STMIA/STMEA + Bit W = _.BIT21(instruction); + uint n = Reg16(16); + uint register_list = instruction & 0xFFFF; + bool wback = W == 1; + if (n == 15 || _.BitCount(register_list) < 1) unpredictable = true; + return ExecuteCore_STM_STMIA_STMEA(Encoding.A1, wback, n, register_list); + } + + uint Execute_STMDB_STMFD_A1() + { + //A8.6.191 STMDB/STMFD + Bit W = _.BIT21(instruction); + uint n = Reg16(16); + uint register_list = instruction & 0xFFFF; + if (W == 1 && n == _.b1101 && _.BitCount(register_list) >= 2) return Execute_PUSH_A1(); + bool wback = W == 1; + if (n == 15 || _.BitCount(register_list) < 1) unpredictable = true; + return ExecuteCore_STMDB_STMFD(Encoding.A1, wback, n, register_list); + } + + uint Execute_BL_A1() + { + //A8.6.23 + //A8-58 + uint imm24 = instruction & 0xFFFFFF; + int imm32 = _SignExtend_32(26, imm24 << 2); + return ExecuteCore_BL_BLX_immediate(Encoding.A1, EInstrSet.ARM, imm32, false); + } + + uint Execute_B_A1() + { + uint imm24 = instruction & 0xFFFFFF; + int imm32 = _SignExtend_32(26, imm24 << 2); + + return ExecuteCore_B(Encoding.A1, imm32); + } + + Decoder Decoder_ExecuteArm_SVCAndCP = new Decoder(); + uint ExecuteArm_SVCAndCP() + { + //A5.6 Supervisor Call, and coprocessor instructions + + Decoder_ExecuteArm_SVCAndCP.Ensure(() => Decoder_ExecuteArm_SVCAndCP + .d("op1", 6).d("op", 1).d("coproc_special", 1).d("rn_is_15", 1) + .r("op1==#0xxxxx && op1!=#000x0x && coproc_special==#1", () => Execute_Unhandled("ExecuteArm_SIMD_VFP_LoadStore")) + .r("op1==#0xxxx0 && op1!=#000x0x && coproc_special==#0", () => Execute_Unhandled("STC,STC2")) + .r("op1==#0xxxx1 && op1!=#000x0x && coproc_special==#0 && rn_is_15==#0", () => Execute_Unhandled("LDC,LDC2(immediate)")) + .r("op1==#0xxxx1 && op1!=#000x0x && coproc_special==#0 && rn_is_15==#1", () => Execute_Unhandled("LDC,LDC2(literal)")) + .r("op1==#00000x", () => Execute_Undefined()) + .r("op1==#00010x && coproc_special==#1", () => Execute_Unhandled("ExecuteArm_SIMD_VFP_64bit_xfer")) + .r("op1==#000100 && coproc_special==#0", () => Execute_Unhandled("MCRR,MCRR2")) + .r("op1==#000101 && coproc_special==#0", () => Execute_Unhandled("MRRC,MRRC2")) + .r("op1==#10xxxx && op==0 && coproc_special==#1", () => Execute_Unhandled("VFP data-processing on page A7-24")) + .r("op1==#10xxxx && op==0 && coproc_special==#0", () => Execute_Unhandled("CDP,CDP2 on page A8-68")) + .r("op1==#10xxxx && op==1 && coproc_special==#1", () => ExecuteArm_ShortVFPTransfer()) + .r("op1==#10xxx0 && op==1 && coproc_special==#0", () => Execute_Unhandled("MCR,MCR2 on pageA8-186")) + .r("op1==#10xxx1 && op==1 && coproc_special==#0", () => Execute_MRC_MRC2_A1()) + .r("op1==#110000", () => Execute_SVC_A1()) + ); + + uint op1 = (instruction >> 20) & 0x3F; + uint Rn = Reg16(16); + uint coproc = (instruction >> 8) & 0xF; + uint op = _.BIT4(instruction); + uint coproc_special = (coproc == _.b1010 || coproc == _.b1011) ? 1U : 0U; + uint rn_is_15 = (Rn == 15) ? 1U : 0U; + + Decoder_ExecuteArm_SVCAndCP.Evaluate(op1, op, coproc_special, rn_is_15); + return 1; + } + + uint Execute_MRC_MRC2_A1() + { + //ignoring admonition to see "advanced SIMD and VFP" which has been handled by decode earlier + uint t = Reg16(12); + uint cp = Reg16(8); + uint opc1 = Reg8(21); + uint crn = Reg16(16); + uint opc2 = Reg8(5); + uint crm = Reg16(0); + if (t == 13 && _CurrentInstrSet() != EInstrSet.ARM) unpredictable = true; + return ExecuteCore_MRC_MRC2(Encoding.A1, cp, opc1, t, crn, crm, opc2); + } + + Decoder Decoder_ExecuteArm_ShortVFPTransfer = new Decoder(); + uint ExecuteArm_ShortVFPTransfer() + { + //A7.8 + //A7-31 + + Decoder_ExecuteArm_ShortVFPTransfer.Ensure(() => Decoder_ExecuteArm_ShortVFPTransfer + .d("A", 3).d("L", 1).d("C", 1).d("B", 2) + .r("L==0 && C==0 && A==#000", () => Execute_Unhandled("VMOV (between ARM core register and single-precision register) on page A8-648")) + .r("L==0 && C==0 && A==#111", () => ExecuteArm_VMSR_A1()) + .r("L==0 && C==1 && A==#0xx", () => Execute_Unhandled("VMOV (ARM core register to scalar) on page A8-644")) + .r("L==0 && C==1 && A==#1xx && B==#0x", () => Execute_Unhandled("VDUP (ARM core register) on page A8-594")) + .r("L==1 && C==0 && A==#000", () => Execute_Unhandled("VMOV (between ARM core register and single-precision register) on page A8-648")) + .r("L==1 && C==0 && A==#111", () => Execute_Unhandled("VMRS on page A8-658 or page B6-27")) + .r("L==1 && C==1", () => Execute_Unhandled("VMOV (scalar to ARM core register) on page A8-646")) + ); + + uint A = (instruction >> 21) & _.b111; + uint L = _.BIT20(instruction); + uint C = _.BIT8(instruction); + uint B = (instruction >> 5) & _.b11; + + Decoder_ExecuteArm_ShortVFPTransfer.Evaluate(A, L, C, B); + return 1; + } + + uint ExecuteArm_VMSR_A1() + { + uint t = Reg16(12); + if (t == 15 || (t == 13 && _CurrentInstrSet() != EInstrSet.ARM)) return _UNPREDICTABLE(); + + if (disassemble) + if (nstyle) + return DISNEW("FMXR", ",", t); + else + return DISNEW("VMSR", ", ", t); + + return ExecuteCore_VMSR(t); + } + + uint Execute_SVC_A1() + { + //A8.6.218 + //A8-430 + uint imm24 = instruction & 0xFFFFFF; + uint imm32 = imm24; + + return ExecuteCore_SVC(Encoding.A1, imm32); + } + + + } //class ARM11 +} \ No newline at end of file diff --git a/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteCore.cs b/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteCore.cs new file mode 100644 index 0000000000..c921f4c8f6 --- /dev/null +++ b/BizHawk.Emulation/CPUs/ARM/ARM.ExecuteCore.cs @@ -0,0 +1,998 @@ +using System; +using System.Diagnostics; + +namespace BizHawk.Emulation.CPUs.ARM +{ + + partial class ARM + { + //A8.6.4 ADD + uint ExecuteCore_ADD_immediate_thumb(Encoding encoding, uint n, uint d, bool setflags, uint imm32) + { + if (disassemble) + if (disopt.showExplicitAccumulateRegisters || n != d) + return DISNEW("ADD", ", , #", setflags, false, d, n, imm32); + else + return DISNEW("ADD", ", #", setflags, false, d, imm32); + + _AddWithCarry32(r[n], imm32, 0); + r[d] = alu_result_32; + if (setflags) + { + APSR.N = _.BIT31(alu_result_32); + APSR.Z = _IsZeroBit(alu_result_32); + APSR.C = alu_carry_out; + APSR.V = alu_overflow; + } + + return 1; + } + + //A8.6.5 ADD (immediate, ARM) + uint ExecuteCore_ADD_immediate_arm(Encoding encoding, bool setflags, uint n, uint d, uint imm32) + { + if (disassemble) + if (disopt.showExplicitAccumulateRegisters || n != d) + return DISNEW("ADD", ", , #", setflags, d, n, imm32); + else + return DISNEW("ADD", ", #", setflags, d, imm32); + + _AddWithCarry32(r[n], imm32, 0); + if (d == 15) + _ALUWritePC(alu_result_32); //"setflags is always FALSE here" + else + { + r[d] = alu_result_32; + if (setflags) + { + APSR.N = _.BIT31(alu_result_32); + APSR.Z = _IsZeroBit(alu_result_32); + APSR.C = alu_carry_out; + APSR.V = alu_overflow; + } + } + + return 1; + } + + //A8.6.6 ADD (register) + uint ExecuteCore_ADD_register(Encoding encoding, uint m, uint d, uint n, bool setflags, SRType shift_t, int shift_n) + { + if (disassemble) + return DISNEW("ADD", ", , <{, shift}>", setflags, d, n, m, shift_t, shift_n); + + uint shifted = _Shift(r[m], shift_t, shift_n, APSR.C); + _AddWithCarry32(r[n], shifted, 0); + if (d == 15) _ALUWritePC(alu_result_32); + else + { + r[d] = alu_result_32; + if (setflags) + { + APSR.N = _.BIT31(alu_result_32); + APSR.Z = _IsZeroBit(alu_result_32); + APSR.C = alu_carry_out; + APSR.V = alu_overflow; + } + } + return 0; + } + + //A8.6.8 ADD (SP plus immediate) + uint ExecuteCore_ADD_SP_plus_immedate(Encoding encoding, uint d, bool setflags, uint imm32) + { + if (disassemble) + return DISNEW("ADD", ", , #", setflags, d, imm32); + + if (setflags && d == 15) throw new NotImplementedException("see SUBS PC, LR and related instruction on page B6-25"); + //addendum about !s && d==PC is correctly handled already by writing pseudocode + _AddWithCarry32(SP, imm32, 0); + if (d == 15) + { + Debug.Assert(!setflags); + _ALUWritePC(alu_result_32); //"can only occur for ARM encoding. setflags is always FALSE here" + } + else + { + r[d] = alu_result_32; + if (setflags) + { + APSR.N = _.BIT31(alu_result_32); + APSR.Z = _IsZeroBit(alu_result_32); + APSR.C = alu_carry_out; + APSR.V = alu_overflow; + } + } + + return 1; + } + + //A8.6.10 ADR + uint ExecuteCore_ADR(Encoding encoding, uint d, uint imm32, bool add) + { + uint result; + if (add) + result = _Align(PC, 4) + imm32; + else result = _Align(PC, 4) - imm32; + + if (disassemble) + if (nstyle) + return DIS("ADD", "/r0/,pc,/imm8_1/ ; maybe MOV rX,pc", d, imm32); + else return DIS("ADR", "/r0/, /label1/ ; maybe MOV rX,pc", d, result); + + if (d == 15) + _ALUWritePC(result); + else r[d] = result; + return 1; + } + + //A8.6.14 ASR (immediate) + uint ExecuteCore_ASR_immediate(Encoding encoding, uint d, uint m, bool setflags, int shift_n) + { + if (disassemble) + return DISNEW("ASR", "<{rd!~rm, }>, #", setflags, d, m, m, shift_n); + + uint result; + Bit carry; + _Shift_C(r[m], SRType.ASR, shift_n, APSR.C, out result, out carry); + if (d == 15) + _ALUWritePC(result); + else + { + r[d] = result; + if (setflags) + { + APSR.N = _.BIT31(result); + APSR.Z = _IsZeroBit(result); + APSR.C = carry; + //APSR.V unchanged + } + } + + return 0; + } + + //A8.6.16 B + uint ExecuteCore_B(Encoding encoding, int imm32) + { + uint label = (uint)((int)PC + imm32); + + if (disassemble) + return DISNEW("B", "