1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Text; 9 using System.Linq; 10 using System.Collections.Generic; 11 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure; 14 using Antmicro.Renode.Exceptions; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Renode.Peripherals.Bus; 17 using Antmicro.Renode.Peripherals.Memory; 18 using Antmicro.Renode.Utilities; 19 using ELFSharp.ELF; 20 21 namespace Antmicro.Renode.Peripherals.CPU 22 { 23 public class MSP430X : BaseCPU, IGPIOReceiver, ICpuSupportingGdb 24 { MSP430X(IMachine machine, string cpuType)25 public MSP430X(IMachine machine, string cpuType) : base(0, cpuType, machine, Endianess.LittleEndian) 26 { 27 // NOTE: Track all ArrayMemory instances for the direct access 28 machine.PeripheralsChanged += (_, ev) => 29 { 30 if(ev.Peripheral is ArrayMemory arrayMemory) 31 { 32 if(ev.Operation == PeripheralsChangedEventArgs.PeripheralChangeType.Removal || 33 ev.Operation == PeripheralsChangedEventArgs.PeripheralChangeType.CompleteRemoval || 34 ev.Operation == PeripheralsChangedEventArgs.PeripheralChangeType.Moved) 35 { 36 foreach(var startingPoint in arrayMemoryList.Where(keyValue => keyValue.Value == arrayMemory).Select(keyValue => keyValue.Key).ToList()) 37 { 38 arrayMemoryList.Remove(startingPoint); 39 } 40 } 41 42 if(ev.Operation == PeripheralsChangedEventArgs.PeripheralChangeType.Addition || 43 ev.Operation == PeripheralsChangedEventArgs.PeripheralChangeType.Moved) 44 { 45 foreach(IBusRegistration registrationPoint in machine.GetPeripheralRegistrationPoints(machine.SystemBus, arrayMemory)) 46 { 47 arrayMemoryList[registrationPoint.StartingPoint] = arrayMemory; 48 } 49 } 50 } 51 }; 52 } 53 Reset()54 public override void Reset() 55 { 56 base.Reset(); 57 58 foreach(var register in Enum.GetValues(typeof(Registers)).Cast<Registers>()) 59 { 60 SetRegisterValue(register, 0); 61 } 62 63 hooks.Clear(); 64 pendingWatchpoints.Clear(); 65 pendingInterrupt.Clear(); 66 } 67 OnGPIO(int number, bool value)68 public void OnGPIO(int number, bool value) 69 { 70 if(value) 71 { 72 pendingInterrupt.Add(number); 73 } 74 else 75 { 76 pendingInterrupt.Remove(number); 77 } 78 } 79 AddHookAtInterruptBegin(Action<ulong> hook)80 public void AddHookAtInterruptBegin(Action<ulong> hook) 81 { 82 throw new RecoverableException("This feature is not implemented yet"); 83 } 84 AddHookAtInterruptEnd(Action<ulong> hook)85 public void AddHookAtInterruptEnd(Action<ulong> hook) 86 { 87 throw new RecoverableException("This feature is not implemented yet"); 88 } 89 AddHookAtWfiStateChange(Action<bool> hook)90 public void AddHookAtWfiStateChange(Action<bool> hook) 91 { 92 throw new RecoverableException("This feature is not implemented yet"); 93 } 94 AddHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook)95 public void AddHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook) 96 { 97 if(!hooks.ContainsKey(addr)) 98 { 99 hooks.Add(addr, new HashSet<Action<ICpuSupportingGdb, ulong>>()); 100 } 101 hooks[addr].Add(hook); 102 } 103 RemoveHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook)104 public void RemoveHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook) 105 { 106 if(!hooks.ContainsKey(addr)) 107 { 108 return; 109 } 110 111 hooks[addr].Remove(hook); 112 } 113 RemoveHooksAt(ulong addr)114 public void RemoveHooksAt(ulong addr) 115 { 116 if(!hooks.ContainsKey(addr)) 117 { 118 return; 119 } 120 121 hooks[addr].Clear(); 122 } 123 RemoveAllHooks()124 public void RemoveAllHooks() 125 { 126 hooks.Clear(); 127 } 128 SetRegister(int register, RegisterValue value)129 public void SetRegister(int register, RegisterValue value) 130 { 131 SetRegisterValue((Registers)register, (uint)value.RawValue); 132 } 133 SetRegisterUnsafe(int register, RegisterValue value)134 public void SetRegisterUnsafe(int register, RegisterValue value) 135 { 136 SetRegister(register, value); 137 } 138 GetRegister(int register)139 public RegisterValue GetRegister(int register) 140 { 141 return RegisterValue.Create(GetRegisterValue((Registers)register), 32); 142 } 143 GetRegisterUnsafe(int register)144 public RegisterValue GetRegisterUnsafe(int register) 145 { 146 return GetRegister(register); 147 } 148 GetRegisters()149 public IEnumerable<CPURegister> GetRegisters() 150 { 151 return Enumerable.Range(0, 16).Select(idx => new CPURegister(idx, 32, isGeneral: true, isReadonly: false)); 152 } 153 EnterSingleStepModeSafely(HaltArguments args)154 public void EnterSingleStepModeSafely(HaltArguments args) 155 { 156 ExecutionMode = ExecutionMode.SingleStep; 157 } 158 159 public override string Architecture => "msp430x"; 160 161 public override RegisterValue PC { get; set; } 162 163 public override ulong ExecutedInstructions => executedInstructions; 164 165 public event Action<int> InterruptAcknowledged; 166 167 public string GDBArchitecture => "MSP430X"; 168 public List<GDBFeatureDescriptor> GDBFeatures => new List<GDBFeatureDescriptor>(); 169 170 public int StackDumpLength { get; set; } = 15; 171 172 public RegisterValue SP { get; set; } 173 public RegisterValue SR 174 { 175 get => (uint)statusRegister; 176 set => statusRegister = (StatusFlags)value.RawValue; 177 } 178 179 public RegisterValue R0 => PC; 180 public RegisterValue R1 => SP; 181 public RegisterValue R2 => SR; 182 // NOTE: Skipping R3/CG register as it's value depends on the opcode 183 public RegisterValue R4 { get; set; } 184 public RegisterValue R5 { get; set; } 185 public RegisterValue R6 { get; set; } 186 public RegisterValue R7 { get; set; } 187 public RegisterValue R8 { get; set; } 188 public RegisterValue R9 { get; set; } 189 public RegisterValue R10 { get; set; } 190 public RegisterValue R11 { get; set; } 191 public RegisterValue R12 { get; set; } 192 public RegisterValue R13 { get; set; } 193 public RegisterValue R14 { get; set; } 194 public RegisterValue R15 { get; set; } 195 DumpRegisters()196 public string DumpRegisters() 197 { 198 StringBuilder sb = new StringBuilder(); 199 for(var i = 0; i < 16; ++i) 200 { 201 if(i > 0) 202 { 203 sb.Append(", "); 204 } 205 var val = GetRegisterValue((Registers)i); 206 sb.AppendFormat("{0}=0x{1:X05}", Enum.GetName(typeof(Registers), i), val); 207 } 208 return sb.ToString(); 209 } 210 DumpStack(int length)211 public string[] DumpStack(int length) 212 { 213 for(var i = 0; i < length; ++i) 214 { 215 this.Log(LogLevel.Debug, "0x{0:X05}: {1:X04}", SP + 2 * i, machine.SystemBus.ReadWord(SP + 2 * (ulong)i)); 216 } 217 218 return Enumerable 219 .Range(0, length) 220 .Select(i => "0x{0:X05}: {1:X04}".FormatWith(SP + 2 * i, machine.SystemBus.ReadWord(SP + 2 * (ulong)i))) 221 .ToArray() 222 ; 223 } 224 ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions)225 public override ExecutionResult ExecuteInstructions(ulong numberOfInstructionsToExecute, out ulong numberOfExecutedInstructions) 226 { 227 numberOfExecutedInstructions = 0; 228 while(numberOfInstructionsToExecute-- > 0) 229 { 230 var result = EvaluateNextOpcode(); 231 if(result == ExecutionResult.Aborted) 232 { 233 this.Log(LogLevel.Error, "Execution aborted"); 234 this.Log(LogLevel.Debug, "Register dump: {0}", DumpRegisters()); 235 this.Log(LogLevel.Debug, "Stack dump (last {0} words)", StackDumpLength); 236 foreach(var dumpLine in DumpStack(StackDumpLength)) 237 { 238 this.Log(LogLevel.Debug, dumpLine); 239 } 240 241 return ExecutionResult.Aborted; 242 } 243 244 numberOfExecutedInstructions++; 245 executedInstructions++; 246 247 if(statusRegister.HasFlag(StatusFlags.GeneralInterruptEnable) && pendingInterrupt.Count > 0) 248 { 249 HandleInterrupt(pendingInterrupt.Min); 250 } 251 252 if(TryHandleWatchpoints()) 253 { 254 pendingWatchpoints.Clear(); 255 return ExecutionResult.StoppedAtWatchpoint; 256 } 257 258 if(hooks.ContainsKey(PC.RawValue)) 259 { 260 return ExecutionResult.StoppedAtBreakpoint; 261 } 262 } 263 264 return ExecutionResult.Ok; 265 } 266 ExecutionFinished(ExecutionResult result)267 protected override bool ExecutionFinished(ExecutionResult result) 268 { 269 if(result == ExecutionResult.StoppedAtBreakpoint) 270 { 271 this.Log(LogLevel.Noisy, "Executing hooks @ {0}", PC); 272 foreach(var hook in hooks[PC.RawValue]) 273 { 274 hook(this, PC.RawValue); 275 } 276 return true; 277 } 278 279 return false; 280 } 281 TryHandleWatchpoints()282 private bool TryHandleWatchpoints() 283 { 284 foreach(var watchpoint in pendingWatchpoints) 285 { 286 machine.SystemBus.TryGetWatchpointsAt(watchpoint.Address, watchpoint.Value.HasValue ? Access.Write : Access.Read, out var watchpoints); 287 foreach(var hook in watchpoints) 288 { 289 hook.Invoke(this, watchpoint.Address, watchpoint.SysbusAccessWidth, watchpoint.Value ?? 0); 290 } 291 } 292 293 return pendingWatchpoints.Count > 0; 294 } 295 SetStatusFlag(StatusFlags flag, bool set)296 private void SetStatusFlag(StatusFlags flag, bool set) 297 { 298 statusRegister = set ? statusRegister | flag : statusRegister & ~flag; 299 } 300 GetRegisterValue(Registers register, AddressingMode addressingMode = AddressingMode.Register)301 private uint GetRegisterValue(Registers register, AddressingMode addressingMode = AddressingMode.Register) 302 { 303 switch(register) 304 { 305 case Registers.PC: 306 return PC; 307 case Registers.SP: 308 return SP; 309 case Registers.SR: 310 switch(addressingMode) 311 { 312 case AddressingMode.Register: 313 return SR; 314 315 case AddressingMode.Indexed: 316 return 0; 317 318 case AddressingMode.IndirectRegister: 319 return 0x0004; 320 321 case AddressingMode.IndirectAutoincrement: 322 return 0x0008; 323 324 default: 325 throw new Exception("unreachable"); 326 } 327 case Registers.R3: 328 switch(addressingMode) 329 { 330 case AddressingMode.Register: 331 return 0; 332 333 case AddressingMode.Indexed: 334 return 0x0001; 335 336 case AddressingMode.IndirectRegister: 337 return 0x0002; 338 339 case AddressingMode.IndirectAutoincrement: 340 return 0xFFFFF; 341 342 default: 343 throw new Exception("unreachable"); 344 } 345 case Registers.R4: 346 return R4; 347 case Registers.R5: 348 return R5; 349 case Registers.R6: 350 return R6; 351 case Registers.R7: 352 return R7; 353 case Registers.R8: 354 return R8; 355 case Registers.R9: 356 return R9; 357 case Registers.R10: 358 return R10; 359 case Registers.R11: 360 return R11; 361 case Registers.R12: 362 return R12; 363 case Registers.R13: 364 return R13; 365 case Registers.R14: 366 return R14; 367 case Registers.R15: 368 return R15; 369 default: 370 throw new Exception($"{register} is not a valid register"); 371 } 372 } 373 SetRegisterValue(Registers register, uint value)374 private void SetRegisterValue(Registers register, uint value) 375 { 376 this.Log(LogLevel.Debug, "{0}: 0x{1:X05} -> 0x{2:X05}", register, GetRegisterValue(register), value); 377 switch(register) 378 { 379 case Registers.PC: 380 PC = value; 381 break; 382 case Registers.SP: 383 SP = value; 384 break; 385 case Registers.SR: 386 SR = value; 387 break; 388 case Registers.R3: 389 // NOTE: Write to this register does nothing 390 // NOTE: Compiler uses it to emulate NOP 391 break; 392 case Registers.R4: 393 R4 = value; 394 break; 395 case Registers.R5: 396 R5 = value; 397 break; 398 case Registers.R6: 399 R6 = value; 400 break; 401 case Registers.R7: 402 R7 = value; 403 break; 404 case Registers.R8: 405 R8 = value; 406 break; 407 case Registers.R9: 408 R9 = value; 409 break; 410 case Registers.R10: 411 R10 = value; 412 break; 413 case Registers.R11: 414 R11 = value; 415 break; 416 case Registers.R12: 417 R12 = value; 418 break; 419 case Registers.R13: 420 R13 = value; 421 break; 422 case Registers.R14: 423 R14 = value; 424 break; 425 case Registers.R15: 426 R15 = value; 427 break; 428 default: 429 throw new Exception($"{register} is not a valid register"); 430 } 431 } 432 HandleInterrupt(int interruptNumber)433 private void HandleInterrupt(int interruptNumber) 434 { 435 var interruptVector = InterruptVectorStart - (ulong)interruptNumber * 2U; 436 var interruptAddress = (ushort)PerformMemoryRead(interruptVector, AccessWidth._16bit); 437 438 var statusAndPC = ((PC & 0xF0000U) >> 4) | SR; 439 440 SP -= 2U; 441 PerformMemoryWrite(SP, PC, AccessWidth._16bit); 442 SP -= 2U; 443 PerformMemoryWrite(SP, statusAndPC, AccessWidth._16bit); 444 445 statusRegister &= StatusFlags.SystemClockGenerator0; 446 PC = interruptAddress; 447 448 InterruptAcknowledged?.Invoke(interruptNumber); 449 } 450 GetOperandValue(Registers register, AddressingMode addressingMode, out ulong address, AccessWidth accessWidth = AccessWidth._16bit, uint addressExtension = 0, bool extended = false)451 private uint GetOperandValue(Registers register, AddressingMode addressingMode, out ulong address, AccessWidth accessWidth = AccessWidth._16bit, uint addressExtension = 0, bool extended = false) 452 { 453 address = 0UL; 454 455 // NOTE: Handle CG1 generator 456 if(register == Registers.SR) 457 { 458 switch(addressingMode) 459 { 460 case AddressingMode.IndirectRegister: 461 return 0x00004; 462 463 case AddressingMode.IndirectAutoincrement: 464 return 0x00008; 465 466 default: 467 break; 468 } 469 } 470 471 // NOTE: Handle CG2 generator 472 if(register == Registers.R3) 473 { 474 switch(addressingMode) 475 { 476 case AddressingMode.Register: 477 return 0x00000; 478 479 case AddressingMode.Indexed: 480 return 0x00001; 481 482 case AddressingMode.IndirectRegister: 483 return 0x00002; 484 485 case AddressingMode.IndirectAutoincrement: 486 return 0xFFFFF; 487 488 default: 489 throw new Exception("unreachable"); 490 } 491 } 492 493 switch(addressingMode) 494 { 495 case AddressingMode.Register: 496 return GetRegisterValue(register); 497 498 case AddressingMode.Indexed: 499 { 500 var registerValue = GetRegisterValue(register, addressingMode); 501 var index = PerformMemoryRead(PC, AccessWidth._16bit); 502 PC += 2U; 503 index |= addressExtension; 504 var memoryAddress = (uint)(registerValue + index); 505 if(registerValue < 64.KB() && !extended) 506 { 507 // NOTE: If register value points to lower 64KB, we should truncate the address 508 // This is not applicable for MSP430X instructions 509 memoryAddress &= 0xFFFF; 510 } 511 512 address = (ulong)memoryAddress; 513 return PerformMemoryRead(address, accessWidth); 514 } 515 516 case AddressingMode.IndirectRegister: 517 { 518 uint registerValue = GetRegisterValue(register, addressingMode); 519 registerValue |= addressExtension; 520 address = (ulong)registerValue; 521 return PerformMemoryRead(address, accessWidth); 522 } 523 524 case AddressingMode.IndirectAutoincrement: 525 { 526 uint registerValue = GetRegisterValue(register, addressingMode); 527 var offset = (accessWidth != AccessWidth._8bit) || register == Registers.PC ? 2U : 1U; 528 SetRegisterValue(register, (uint)(registerValue + offset) & 0xFFFFF); 529 530 if(register == Registers.PC) 531 { 532 // NOTE: Immediate addressing (@PC+) 533 // Get immediate value @PC and append the extended address 534 address = (ulong)registerValue; 535 uint immediate = PerformMemoryRead(registerValue, AccessWidth._16bit); 536 immediate |= addressExtension; 537 return immediate; 538 } 539 else 540 { 541 // NOTE: Indirect addressing (@Rn+) 542 // Get address in the register and append the extended address 543 // then access the memory 544 address = (ulong)registerValue | addressExtension; 545 address &= 0xFFFFF; 546 return PerformMemoryRead(address, accessWidth); 547 } 548 } 549 550 default: 551 throw new Exception("unreachable"); 552 } 553 } 554 EvaluateOpcodeJump(ushort instr)555 private ExecutionResult EvaluateOpcodeJump(ushort instr) 556 { 557 // NOTE: Jump instructions 558 var opcodeCondition = (instr & 0x1C00) >> 10; 559 560 // NOTE: Offset is sign extended 561 var offset = (uint)(instr & 0x3FF); 562 offset |= (offset & 0x200) > 0 ? offset | 0xFFFFFC00 : 0; 563 var offsetSigned = (int)offset * 2; 564 var shouldJump = false; 565 566 switch(opcodeCondition) 567 { 568 case 0x00: 569 // NOTE: JNE, JNZ 570 shouldJump = !statusRegister.HasFlag(StatusFlags.Zero); 571 break; 572 573 case 0x01: 574 // NOTE: JEQ, JZ 575 shouldJump = statusRegister.HasFlag(StatusFlags.Zero); 576 break; 577 578 case 0x02: 579 // NOTE: JNC 580 shouldJump = !statusRegister.HasFlag(StatusFlags.Carry); 581 break; 582 583 case 0x03: 584 // NOTE: JC 585 shouldJump = statusRegister.HasFlag(StatusFlags.Carry); 586 break; 587 588 case 0x04: 589 // NOTE: JN 590 shouldJump = statusRegister.HasFlag(StatusFlags.Negative); 591 break; 592 593 case 0x05: 594 // NOTE: JGE 595 // NOTE: Negative ^ Overflow == False 596 shouldJump = !(statusRegister.HasFlag(StatusFlags.Negative) ^ statusRegister.HasFlag(StatusFlags.Overflow)); 597 break; 598 599 case 0x06: 600 // NOTE: JL 601 // NOTE: Negative ^ Overflow == True 602 shouldJump = statusRegister.HasFlag(StatusFlags.Negative) ^ statusRegister.HasFlag(StatusFlags.Overflow); 603 break; 604 605 case 0x07: 606 // NOTE: JMP 607 // NOTE: Jump unconditionally 608 shouldJump = true; 609 break; 610 611 default: 612 return ExecutionResult.Aborted; 613 } 614 615 if(shouldJump) 616 { 617 PC = (uint)((long)PC + (long)offsetSigned); 618 } 619 return ExecutionResult.Ok; 620 } 621 TryEvaluateSingleOperand(ushort instr, int destination, AccessWidth accessWidth, AddressingMode addressingMode, out ExecutionResult executionResult, uint addressExtension = 0, int repetition = 1, bool resetCarry = false, bool extended = false)622 private bool TryEvaluateSingleOperand(ushort instr, int destination, AccessWidth accessWidth, AddressingMode addressingMode, out ExecutionResult executionResult, uint addressExtension = 0, int repetition = 1, bool resetCarry = false, bool extended = false) 623 { 624 executionResult = ExecutionResult.Aborted; 625 626 if((instr & 0xFC00) >= 0x2000) 627 { 628 return false; 629 } 630 631 for(; repetition > 0; --repetition) 632 { 633 if(resetCarry) 634 { 635 SetStatusFlag(StatusFlags.Carry, false); 636 } 637 638 var opc = instr & 0xFC00; 639 if(opc < 0x1000) 640 { 641 var funcIdentifier = (instr & 0x00F0) >> 4; 642 // NOTE: First eight instructions implements different variants of MOV.A, and bit-shift functions 643 // Second half implements CMP.A, ADD.A, SUB.A and MOV.A 644 645 switch(funcIdentifier) 646 { 647 case 0: 648 { 649 // NOTE: MOVA @Rsrc 650 var sourceRegister = (Registers)((instr & 0x0F00) >> 8); 651 var memoryAddress = GetRegisterValue(sourceRegister, AddressingMode.Register); 652 var memoryValue = PerformMemoryRead(memoryAddress, AccessWidth._20bit); 653 654 SetRegisterValue((Registers)destination, memoryValue); 655 continue; 656 } 657 case 1: 658 { 659 // NOTE: MOVA @Rsrc+ 660 var sourceRegister = (Registers)((instr & 0x0F00) >> 8); 661 var memoryAddress = GetRegisterValue(sourceRegister, AddressingMode.Register); 662 SetRegisterValue(sourceRegister, memoryAddress + 4); 663 664 var memoryValue = PerformMemoryRead(memoryAddress, AccessWidth._20bit); 665 SetRegisterValue((Registers)destination, memoryValue); 666 continue; 667 } 668 case 2: 669 { 670 // NOTE: MOVA &abs20 671 var absoluteAddress = (uint)GetOperandValue(Registers.PC, AddressingMode.IndirectAutoincrement, out _); 672 absoluteAddress |= (uint)(instr & 0x0F00) << 8; 673 674 var memoryValue = PerformMemoryRead(absoluteAddress, AccessWidth._20bit); 675 SetRegisterValue((Registers)destination, memoryValue); 676 continue; 677 } 678 case 3: 679 { 680 // NOTE: MOVA z16(Rsrc) 681 var sourceRegister = (Registers)((instr & 0x0F00) >> 8); 682 var offset = (short)GetOperandValue(Registers.PC, AddressingMode.IndirectAutoincrement, out _); 683 var memoryAddress = (ulong)(GetRegisterValue(sourceRegister) + offset); 684 685 var memoryValue = PerformMemoryRead(memoryAddress, AccessWidth._20bit); 686 SetRegisterValue((Registers)destination, memoryValue); 687 continue; 688 } 689 case 4: 690 case 5: 691 { 692 // NOTE: RRCM/RRAM/RLAM/RRUM 693 var instructionWidth = (instr & 0x0010) > 0 ? AccessWidth._16bit : AccessWidth._20bit; 694 var bitLocation = ((instr & 0x0C00) >> 10) + 1; 695 var func = (instr & 0x0300) >> 8; 696 var registerValue = GetRegisterValue((Registers)destination); 697 var width = GetAccessWidthInBits(instructionWidth); 698 699 switch(func) 700 { 701 case 0: 702 { 703 // NOTE: RRCM 704 var shouldCarry = (registerValue & (1 << (bitLocation - 1))) > 0; 705 706 registerValue >>= bitLocation; 707 registerValue |= statusRegister.HasFlag(StatusFlags.Carry) ? (1U << (width - bitLocation)) : 0U; 708 709 SetStatusFlag(StatusFlags.Carry, shouldCarry); 710 TruncateWithFlags((uint)registerValue, accessWidth); 711 SetRegisterValue((Registers)destination, registerValue); 712 break; 713 } 714 case 1: 715 { 716 // NOTE: RRAM 717 var signExtension = ((1U << (bitLocation + 1)) - 1) << (width - bitLocation); 718 signExtension = (registerValue & GetAccessWidthMSB(instructionWidth)) > 0 ? signExtension : 0; 719 720 var shouldCarry = (registerValue & (1 << (bitLocation - 1))) > 0; 721 registerValue >>= bitLocation; 722 registerValue |= signExtension; 723 TruncateWithFlags(registerValue); 724 SetStatusFlag(StatusFlags.Carry, shouldCarry); 725 SetRegisterValue((Registers)destination, registerValue); 726 break; 727 } 728 case 2: 729 { 730 // NOTE: RLAM 731 var shouldCarry = (registerValue & (1 << (width - bitLocation))) > 0; 732 registerValue <<= bitLocation; 733 TruncateWithFlags(registerValue); 734 SetStatusFlag(StatusFlags.Carry, shouldCarry); 735 SetRegisterValue((Registers)destination, registerValue); 736 break; 737 } 738 case 3: 739 { 740 // NOTE: RRUM 741 var shouldCarry = (registerValue & (1 << (bitLocation - 1))) > 0; 742 registerValue >>= bitLocation; 743 TruncateWithFlags(registerValue); 744 SetStatusFlag(StatusFlags.Carry, shouldCarry); 745 SetRegisterValue((Registers)destination, registerValue); 746 break; 747 } 748 } 749 750 continue; 751 } 752 case 6: 753 { 754 // NOTE: MOVA @Rsrc, &abs20 755 var sourceRegister = (Registers)((instr & 0x0F00) >> 8); 756 var value = GetRegisterValue(sourceRegister, AddressingMode.Register); 757 var offset = (short)GetOperandValue(Registers.PC, AddressingMode.IndirectAutoincrement, out _); 758 var memoryAddress = (ulong)(destination + offset); 759 PerformMemoryWrite(memoryAddress, value, AccessWidth._20bit); 760 continue; 761 } 762 case 7: 763 { 764 // NOTE: MOVA @Rsrc, z16(Rdst) 765 var sourceRegister = (Registers)((instr & 0x0F00) >> 8); 766 var value = GetRegisterValue(sourceRegister, AddressingMode.Register); 767 var offset = (short)GetOperandValue(Registers.PC, AddressingMode.IndirectAutoincrement, out _); 768 var memoryAddress = (ulong)(GetRegisterValue((Registers)destination) + offset); 769 PerformMemoryWrite(memoryAddress, value, AccessWidth._20bit); 770 continue; 771 } 772 } 773 774 // NOTE: Third bit defines addressing mode 775 var immediateValue = ((funcIdentifier & 0x4) >> 2) == 0; 776 777 uint sourceValue; 778 if(immediateValue) 779 { 780 sourceValue = GetOperandValue(Registers.PC, AddressingMode.IndirectAutoincrement, out _); 781 sourceValue |= (uint)(instr & 0x0F00) << 8; 782 } 783 else 784 { 785 var sourceRegister = (Registers)((instr & 0x0F00) >> 8); 786 var sourceRegisterAddressing = AddressingMode.Register; 787 788 // NOTE: ADDA R2/3, Rdst and SUBA R2/3, Rdst should read the CG in indirect register mode 789 if((sourceRegister == Registers.SR || sourceRegister == Registers.R3) && (funcIdentifier & 0xE) == 0xE) 790 { 791 sourceRegisterAddressing = AddressingMode.IndirectRegister; 792 } 793 sourceValue = GetRegisterValue(sourceRegister, sourceRegisterAddressing); 794 } 795 796 switch(funcIdentifier & 0x3) 797 { 798 case 0: 799 { 800 // NOTE: MOVA #imm20 801 // NOTE: MOVA @Rsrc 802 SetRegisterValue((Registers)destination, sourceValue); 803 break; 804 } 805 case 1: 806 { 807 // NOTE: CMPA #imm20 808 // NOTE: CMPA @Rsrc 809 var destinationValue = GetRegisterValue((Registers)destination); 810 811 var cmpTemp = (sourceValue ^ 0xFFFFF) & 0xFFFFF; 812 cmpTemp = cmpTemp + destinationValue + 1; 813 cmpTemp = TruncateWithFlags(cmpTemp, AccessWidth._20bit); 814 CheckForOverflow(sourceValue, cmpTemp, destinationValue, AccessWidth._20bit); 815 break; 816 } 817 case 2: 818 { 819 // NOTE: ADDA #imm20 820 // NOTE: ADDA @Rsrc 821 var destinationValue = GetRegisterValue((Registers)destination); 822 823 var calculatedValue = sourceValue + destinationValue; 824 calculatedValue = TruncateWithFlags(calculatedValue, AccessWidth._20bit); 825 CheckForOverflow(sourceValue, destinationValue, calculatedValue, AccessWidth._20bit); 826 SetRegisterValue((Registers)destination, calculatedValue); 827 break; 828 } 829 case 3: 830 { 831 // NOTE: SUBA #imm20 832 // NOTE: SUBA @Rsrc 833 var destinationValue = GetRegisterValue((Registers)destination); 834 835 sourceValue = (sourceValue ^ 0xFFFFF) & 0xFFFFF; 836 sourceValue += 1; 837 838 var calculatedValue = sourceValue + destinationValue; 839 calculatedValue = TruncateWithFlags(calculatedValue, AccessWidth._20bit); 840 CheckForOverflow(sourceValue, destinationValue, calculatedValue, AccessWidth._20bit); 841 SetRegisterValue((Registers)destination, calculatedValue); 842 break; 843 } 844 default: 845 return true; 846 } 847 848 continue; 849 } 850 else if(opc < 0x1400) 851 { 852 // NOTE: Single operand instructions 853 var fullOpcode = (instr & 0x0380) >> 7; 854 855 // NOTE: CALLA (20-bit), handle this separately 856 switch(fullOpcode) 857 { 858 case 0x06: 859 { 860 uint newPC; 861 // NOTE: RETI / CALLA 862 if((Registers)destination == Registers.PC) 863 { 864 // NOTE: RETI 865 var statusAndPC = PerformMemoryRead(SP, AccessWidth._16bit); 866 statusRegister = (StatusFlags)(statusAndPC & 0x1FF); 867 SP += 2U; 868 869 newPC = PerformMemoryRead(SP, AccessWidth._16bit); 870 SP += 2U; 871 newPC |= (uint)((statusAndPC & 0xF000) << 4); 872 PC = newPC; 873 } 874 else 875 { 876 // NOTE: CALLA 877 // NOTE: Decrement SP before reading the address 878 SP -= 2; 879 newPC = GetOperandValue((Registers)destination, addressingMode, out _, accessWidth: AccessWidth._20bit, addressExtension: addressExtension, extended: extended); 880 881 SP -= 2; 882 PerformMemoryWrite(SP, PC, AccessWidth._20bit); 883 PC = (uint)newPC; 884 } 885 continue; 886 } 887 888 case 0x07: 889 { 890 // NOTE: `register` contains part of the address 891 var fullAddress = (uint)destination << 16; 892 SP -= 2U; 893 var imm = GetOperandValue(Registers.PC, AddressingMode.IndirectAutoincrement, out var _); 894 fullAddress |= imm; 895 896 SP -= 2U; 897 PerformMemoryWrite(SP, PC, AccessWidth._20bit); 898 899 switch(addressingMode) 900 { 901 case AddressingMode.Register: 902 // NOTE: Absolute addressing 903 fullAddress = PerformMemoryRead(fullAddress, AccessWidth._20bit); 904 PC = (uint)fullAddress; 905 break; 906 907 case AddressingMode.Indexed: 908 // TODO: Indexed addressing 909 this.Log(LogLevel.Error, "CALLA indexed addressing is not supported"); 910 return true; 911 912 case AddressingMode.IndirectAutoincrement: 913 // NOTE: Immediate addressing 914 PC = (uint)fullAddress; 915 break; 916 917 default: 918 return true; 919 } 920 921 continue; 922 } 923 924 } 925 926 switch(fullOpcode) 927 { 928 case 0x04: // NOTE: PUSH 929 case 0x05: // NOTE: CALL 930 // NOTE: PUSH and CALL decrement stack pointer before operand evaluation 931 SP -= 2U; 932 break; 933 934 default: 935 // NOTE: Do nothing 936 break; 937 } 938 939 var operand = GetOperandValue((Registers)destination, addressingMode, out var address, accessWidth: accessWidth, addressExtension: addressExtension, extended: extended); 940 switch(fullOpcode) 941 { 942 case 0x00: 943 { 944 // NOTE: RRC 945 var msb = statusRegister.HasFlag(StatusFlags.Carry) ? GetAccessWidthMSB(accessWidth) : 0; 946 SetStatusFlag(StatusFlags.Carry, (operand & 0x1) > 0); 947 operand = (operand >> 1) | msb; 948 TruncateWithFlags((uint)operand, accessWidth); 949 break; 950 } 951 952 case 0x01: 953 // NOTE: SWPB 954 // NOTE: Status bits are not affected 955 operand = ((operand >> 8) | (operand << 8)) & 0xFFFF; 956 operand &= GetAccessWidthMask(accessWidth); 957 break; 958 959 case 0x02: 960 { 961 // NOTE: RRA 962 var msb = GetAccessWidthMSB(accessWidth); 963 msb = (uint)(operand & msb); 964 SetStatusFlag(StatusFlags.Carry, (operand & 0x1) > 0); 965 operand = msb | (operand >> 1); 966 TruncateWithFlags((uint)operand, accessWidth); 967 break; 968 } 969 970 case 0x03: 971 { 972 // NOTE: SXT 973 var msb = GetAccessWidthMSB(accessWidth); 974 msb = (uint)(operand & msb); 975 operand |= msb > 0 ? 0xFFFF00U : 0U; 976 TruncateWithFlags((uint)operand, accessWidth); 977 SetStatusFlag(StatusFlags.Carry, !statusRegister.HasFlag(StatusFlags.Zero)); 978 break; 979 } 980 981 case 0x04: 982 // NOTE: PUSH 983 PerformMemoryWrite(SP, operand, accessWidth); 984 continue; 985 986 case 0x05: 987 // NOTE: CALL 988 PerformMemoryWrite(SP, PC, AccessWidth._16bit); 989 PC = (uint)operand; 990 continue; 991 992 default: 993 return true; 994 } 995 996 if(addressingMode == AddressingMode.Register) 997 { 998 SetRegisterValue((Registers)destination, (uint)operand); 999 } 1000 else 1001 { 1002 PerformMemoryWrite(address, operand, accessWidth); 1003 } 1004 } 1005 else if(opc < 0x1800) 1006 { 1007 // NOTE: MSP430X stack instructions 1008 var n = 1 + ((instr & 0x00F0) >> 4); 1009 1010 switch((instr & 0x0300) >> 8) 1011 { 1012 case 0: 1013 // NOTE: PUSHM.A 1014 // NOTE: Check if the instruction is correct, otherwise abort CPU 1015 if(destination < n - 1) 1016 { 1017 this.Log(LogLevel.Error, "Tried to push {0} registers, starting from {1} which is illegal; compilator bug?", n, (Registers)destination); 1018 executionResult = ExecutionResult.Aborted; 1019 return true; 1020 } 1021 1022 for(var reg = destination; n != 0; n--, reg--) 1023 { 1024 var registerValue = GetRegisterValue((Registers)reg, AddressingMode.Register); 1025 SP -= 2U; 1026 PerformMemoryWrite(SP, (ushort)(registerValue >> 16), AccessWidth._16bit); 1027 SP -= 2U; 1028 PerformMemoryWrite(SP, (ushort)registerValue, AccessWidth._16bit); 1029 } 1030 break; 1031 case 1: 1032 // NOTE: PUSHM.W 1033 // NOTE: Check if the instruction is correct, otherwise abort CPU 1034 if(destination < n - 1) 1035 { 1036 this.Log(LogLevel.Error, "Tried to push {0} registers, starting from {1} which is illegal; compilator bug?", n, (Registers)destination); 1037 executionResult = ExecutionResult.Aborted; 1038 return true; 1039 } 1040 1041 for(var reg = destination; n != 0; n--, reg--) 1042 { 1043 var registerValue = GetRegisterValue((Registers)reg, AddressingMode.Register); 1044 SP -= 2U; 1045 PerformMemoryWrite(SP, (ushort)registerValue, AccessWidth._16bit); 1046 } 1047 break; 1048 case 2: 1049 // NOTE: POPM.A 1050 // NOTE: Check if the instruction is correct, otherwise abort CPU 1051 if(destination + n - 1 > 16) 1052 { 1053 this.Log(LogLevel.Error, "Tried to pop {0} registers, starting from {1} which is illegal; compilator bug?", n, (Registers)destination); 1054 executionResult = ExecutionResult.Aborted; 1055 return true; 1056 } 1057 1058 for(var reg = destination; n != 0; n--, reg++) 1059 { 1060 var registerValue = PerformMemoryRead(SP, AccessWidth._16bit); 1061 SP += 2U; 1062 registerValue |= PerformMemoryRead(SP, AccessWidth._16bit) << 16; 1063 SP += 2U; 1064 SetRegisterValue((Registers)reg, registerValue); 1065 } 1066 break; 1067 case 3: 1068 // NOTE: POPM.W 1069 // NOTE: Check if the instruction is correct, otherwise abort CPU 1070 if(destination + n - 1 > 16) 1071 { 1072 this.Log(LogLevel.Error, "Tried to pop {0} registers, starting from {1} which is illegal; compilator bug?", n, (Registers)destination); 1073 executionResult = ExecutionResult.Aborted; 1074 return true; 1075 } 1076 1077 for(var reg = destination; n != 0; n--, reg++) 1078 { 1079 var registerValue = PerformMemoryRead(SP, AccessWidth._16bit); 1080 SP += 2U; 1081 SetRegisterValue((Registers)reg, registerValue); 1082 this.Log(LogLevel.Noisy, "POPM.W={0:X}", registerValue); 1083 } 1084 break; 1085 } 1086 } 1087 else if(opc < 0x2000) 1088 { 1089 // NOTE: Extension words 1090 executionResult = EvaluateNextOpcode(extensionWord: instr); 1091 return true; 1092 } 1093 } 1094 1095 executionResult = ExecutionResult.Ok; 1096 return true; 1097 } 1098 TryEvaluateDoubleOperand(uint instr, int destination, AccessWidth accessWidth, AddressingMode sourceAddressing, AddressingMode destinationAddressing, out ExecutionResult executionResult, uint destinationExtension = 0, uint sourceExtension = 0, int repetition = 1, bool resetCarry = false, bool extended = false)1099 private bool TryEvaluateDoubleOperand(uint instr, int destination, AccessWidth accessWidth, AddressingMode sourceAddressing, AddressingMode destinationAddressing, out ExecutionResult executionResult, uint destinationExtension = 0, uint sourceExtension = 0, int repetition = 1, bool resetCarry = false, bool extended = false) 1100 { 1101 var opcode = (instr & 0xF000) >> 12; 1102 var source = (instr & 0x0F00) >> 8; 1103 1104 executionResult = ExecutionResult.Aborted; 1105 1106 for(; repetition > 0; --repetition) 1107 { 1108 if(resetCarry) 1109 { 1110 SetStatusFlag(StatusFlags.Carry, false); 1111 } 1112 1113 var operand1 = GetOperandValue((Registers)source, sourceAddressing, out var sourceAddress, accessWidth: accessWidth, addressExtension: sourceExtension, extended: extended); 1114 var operand2 = GetOperandValue((Registers)destination, destinationAddressing, out var destinationAddress, accessWidth: accessWidth, addressExtension: destinationExtension, extended: extended); 1115 1116 var temporaryValue = 0U; 1117 1118 this.Log(LogLevel.Debug, "Operand1=0x{0:X} AddressingMode={1} Operand2=0x{2:X} AddressingMode={3}", operand1, sourceAddressing, operand2, destinationAddressing); 1119 this.Log(LogLevel.Debug, "Operand1 address=0x{0:X} extension=0x{1:X} Operand2 address=0x{2:X} extension=0x{3:X}", sourceAddress, sourceExtension, destinationAddress, destinationExtension); 1120 1121 switch(opcode) 1122 { 1123 case 0x4: 1124 // NOTE: MOV, MOV.B 1125 operand1 &= GetAccessWidthMask(accessWidth); 1126 break; 1127 1128 case 0x5: 1129 // NOTE: ADD, ADD.B 1130 temporaryValue = operand1 + operand2; 1131 temporaryValue = TruncateWithFlags(temporaryValue, accessWidth); 1132 CheckForOverflow(operand1, operand2, temporaryValue, accessWidth); 1133 operand1 = temporaryValue; // XXX: Just use this variable instead of operand1 1134 break; 1135 1136 case 0x6: 1137 // NOTE: ADDC, ADDC.B 1138 temporaryValue = operand1 + operand2 + (statusRegister.HasFlag(StatusFlags.Carry) ? 1U : 0U); 1139 temporaryValue = TruncateWithFlags(temporaryValue, accessWidth); 1140 CheckForOverflow(operand1, operand2, temporaryValue, accessWidth); 1141 operand1 = temporaryValue; 1142 break; 1143 1144 case 0x7: 1145 // NOTE: SUBC, SUBC.B 1146 operand1 = (operand1 ^ GetAccessWidthMask(accessWidth)) & GetAccessWidthMask(accessWidth); 1147 operand1 += statusRegister.HasFlag(StatusFlags.Carry) ? 1U : 0U; 1148 temporaryValue = operand1 + operand2; 1149 temporaryValue = TruncateWithFlags(temporaryValue, accessWidth); 1150 CheckForOverflow(operand1, operand2, temporaryValue, accessWidth); 1151 operand1 = temporaryValue; 1152 break; 1153 1154 case 0x8: 1155 // NOTE: SUB, SUB.B 1156 operand1 = (operand1 ^ GetAccessWidthMask(accessWidth)) & GetAccessWidthMask(accessWidth); 1157 operand1 += 1; 1158 temporaryValue = operand1 + operand2; 1159 temporaryValue = TruncateWithFlags(temporaryValue, accessWidth); 1160 CheckForOverflow(operand1, operand2, temporaryValue, accessWidth); 1161 operand1 = temporaryValue; 1162 break; 1163 1164 case 0x9: 1165 // NOTE: CMP, CMP.B 1166 operand1 = (operand1 ^ GetAccessWidthMask(accessWidth)) & GetAccessWidthMask(accessWidth); 1167 operand1 += 1; 1168 var cmpTemp = operand1 + operand2; 1169 cmpTemp = TruncateWithFlags((uint)cmpTemp, accessWidth); 1170 CheckForOverflow(operand1, operand2, cmpTemp, accessWidth); 1171 continue; 1172 1173 case 0xA: 1174 // NOTE: DADD, DADD.B 1175 1176 // NOTE: Convert operands to binary 1177 operand1 = BCDToBinary(operand1 & GetAccessWidthMask(accessWidth)); 1178 operand2 = BCDToBinary(operand2 & GetAccessWidthMask(accessWidth)); 1179 1180 // NOTE: Add and convert back to BCD 1181 operand1 = operand1 + operand2 + (statusRegister.HasFlag(StatusFlags.Carry) ? 1U : 0U); 1182 1183 var maximumWidth = accessWidth == AccessWidth._20bit ? 99999 : (accessWidth == AccessWidth._16bit ? 9999 : 99); 1184 SetStatusFlag(StatusFlags.Carry, operand1 > maximumWidth); 1185 SetStatusFlag(StatusFlags.Zero, operand1 == 0); 1186 1187 operand1 = BinaryToBCD(operand1); 1188 SetStatusFlag(StatusFlags.Overflow, (operand1 & GetAccessWidthMask(accessWidth)) > 0); 1189 1190 break; 1191 1192 case 0xB: 1193 // NOTE: BIT, BIT.B 1194 var bitTemp = operand1 & operand2; 1195 TruncateWithFlags((uint)bitTemp, accessWidth); 1196 SetStatusFlag(StatusFlags.Carry, !statusRegister.HasFlag(StatusFlags.Zero)); 1197 continue; 1198 1199 case 0xC: 1200 // NOTE: BIC, BIC.B 1201 operand1 = ~operand1 & operand2; 1202 operand1 &= GetAccessWidthMask(accessWidth); 1203 break; 1204 1205 case 0xD: 1206 // NOTE: BIS, BIS.B 1207 operand1 |= operand2; 1208 operand1 &= GetAccessWidthMask(accessWidth); 1209 break; 1210 1211 case 0xE: 1212 // NOTE: XOR, XOR.B 1213 operand1 ^= operand2; 1214 1215 operand1 = TruncateWithFlags((uint)operand1, accessWidth); 1216 SetStatusFlag(StatusFlags.Carry, !statusRegister.HasFlag(StatusFlags.Zero)); 1217 break; 1218 1219 case 0xF: 1220 // NOTE: AND, AND.B 1221 operand1 &= operand2; 1222 1223 operand1 = TruncateWithFlags((uint)operand1, accessWidth); 1224 SetStatusFlag(StatusFlags.Overflow, false); 1225 SetStatusFlag(StatusFlags.Carry, !statusRegister.HasFlag(StatusFlags.Zero)); 1226 break; 1227 1228 default: 1229 // NOTE: Now we have handled all possible instructions, throw error if we are here 1230 this.Log(LogLevel.Error, "Unhandled instruction: 0x{0:X04}", instr); 1231 return false; 1232 } 1233 1234 if(destinationAddressing == 0) 1235 { 1236 SetRegisterValue((Registers)destination, (uint)operand1); 1237 } 1238 else 1239 { 1240 PerformMemoryWrite(destinationAddress, operand1, accessWidth); 1241 } 1242 } 1243 1244 executionResult = ExecutionResult.Ok; 1245 return true; 1246 } 1247 EvaluateNextOpcode(ushort extensionWord = 0)1248 private ExecutionResult EvaluateNextOpcode(ushort extensionWord = 0) 1249 { 1250 var instr = (ushort)PerformMemoryRead((uint)PC, AccessWidth._16bit); 1251 this.Log(LogLevel.Debug, "{0}: 0x{1:X04} @ {2}", PC, instr, ExecutedInstructions); 1252 PC += 2U; 1253 1254 // NOTE: Jump instruction start with either 2XXXh or 3XXXh 1255 var opcode = (instr & 0xF000) >> 12; 1256 if(opcode == 0x2 || opcode == 0x3) 1257 { 1258 return EvaluateOpcodeJump(instr); 1259 } 1260 1261 var accessWidth = ((instr >> 6) & 0x1) > 0 ? AccessWidth._8bit : AccessWidth._16bit; 1262 var sourceAddressing = (AddressingMode)((instr >> 4) & 0x3); 1263 var destinationAddressing = (AddressingMode)((instr >> 7) & 0x1); 1264 var destination = instr & 0x000F; 1265 var repetition = 1; 1266 1267 var sourceExtension = 0U; 1268 var destinationExtension = 0U; 1269 var resetCarry = false; 1270 var extended = extensionWord != 0; 1271 1272 if(extensionWord != 0) 1273 { 1274 this.Log(LogLevel.Noisy, "Current extensions word 0x{0:X}", extensionWord); 1275 var extendedAccess = ((extensionWord >> 6) & 0x1) == 0; 1276 if(extendedAccess && accessWidth == AccessWidth._16bit) 1277 { 1278 this.Log(LogLevel.Warning, "Current instruction has invalid access width configuration (both `A/L` and `B/W` are unset); bug in compilator?"); 1279 return ExecutionResult.Aborted; 1280 } 1281 1282 accessWidth = extendedAccess ? AccessWidth._20bit : accessWidth; 1283 // NOTE: All single operand instructions are in format 0x10xx 1284 var isSingleOperand = (instr & 0xFF00) == 0x1000; 1285 if(sourceAddressing == AddressingMode.Register && (destinationAddressing == AddressingMode.Register || isSingleOperand)) 1286 { 1287 // NOTE: When using Register mode, we have to check for amount of repetition 1288 var repetitionSource = ((extensionWord >> 7) & 0x1) > 0; 1289 if(repetitionSource) 1290 { 1291 // NOTE: Number of `n - 1` repetition is in register 1292 var register = (Registers)(extensionWord & 0x000F); 1293 repetition = (int)GetRegisterValue(register, AddressingMode.Register) & 0xF; 1294 } 1295 else 1296 { 1297 // NOTE: Number of `n - 1` repetition in low 4 bits of the opcode 1298 repetition = extensionWord & 0x000F; 1299 } 1300 repetition += 1; 1301 resetCarry = ((extensionWord >> 8) & 0x1) > 0; 1302 1303 this.Log(LogLevel.Debug, "Repetitions: {0}, sourced from register?: {1}", repetition, repetitionSource); 1304 } 1305 else 1306 { 1307 sourceExtension = ((uint)extensionWord & 0x0780) << 9; 1308 destinationExtension = ((uint)extensionWord & 0x000F) << 16; 1309 } 1310 } 1311 1312 // NOTE: Handle single-operand (Format II) instructions first 1313 // sourceAddressing is used for single-operand instructions 1314 if(TryEvaluateSingleOperand(instr, destination, accessWidth, sourceAddressing, out var executionResult, addressExtension: destinationExtension, repetition: repetition, resetCarry: resetCarry, extended: extended)) 1315 { 1316 return executionResult; 1317 } 1318 1319 // NOTE: Handle double-operand (Format I) instructions 1320 TryEvaluateDoubleOperand(instr, destination, accessWidth, sourceAddressing, destinationAddressing, out executionResult, destinationExtension: destinationExtension, sourceExtension: sourceExtension, repetition: repetition, resetCarry: resetCarry, extended: extended); 1321 1322 return executionResult; 1323 } 1324 BinaryToBCD(uint binary)1325 private uint BinaryToBCD(uint binary) 1326 { 1327 return (((binary / 1) % 10) << 0) | 1328 (((binary / 10) % 10) << 4) | 1329 (((binary / 100) % 10) << 8) | 1330 (((binary / 1000) % 10) << 12) | 1331 (((binary / 10000) % 10) << 16); 1332 } 1333 BCDToBinary(uint bcd)1334 private uint BCDToBinary(uint bcd) 1335 { 1336 return ((bcd >> 0) & 0xf) * 1 + 1337 ((bcd >> 4) & 0xf) * 10 + 1338 ((bcd >> 8) & 0xf) * 100 + 1339 ((bcd >> 12) & 0xf) * 1000 + 1340 ((bcd >> 16) & 0xf) * 10000; 1341 } 1342 TruncateWithFlags(uint value, AccessWidth accessWidth = AccessWidth._16bit)1343 private uint TruncateWithFlags(uint value, AccessWidth accessWidth = AccessWidth._16bit) 1344 { 1345 var mask = GetAccessWidthMask(accessWidth); 1346 1347 SetStatusFlag(StatusFlags.Carry, value > mask); 1348 value &= mask; 1349 SetStatusFlag(StatusFlags.Zero, value == 0); 1350 SetStatusFlag(StatusFlags.Negative, (value & GetAccessWidthMSB(accessWidth)) > 0); 1351 return value; 1352 } 1353 CheckForOverflow(uint operand1, uint operand2, uint result, AccessWidth accessWidth = AccessWidth._16bit)1354 private void CheckForOverflow(uint operand1, uint operand2, uint result, AccessWidth accessWidth = AccessWidth._16bit) 1355 { 1356 var mask = GetAccessWidthMSB(accessWidth); 1357 SetStatusFlag(StatusFlags.Overflow, ((operand1 ^ operand2) & mask) == 0 && ((operand1 ^ result) & mask) > 0); 1358 } 1359 TryPerformDirectWrite(ulong address, uint value, AccessWidth accessWidth)1360 private bool TryPerformDirectWrite(ulong address, uint value, AccessWidth accessWidth) 1361 { 1362 var len = (ulong)GetAccessWidthInBytes(accessWidth); 1363 var keyValue = arrayMemoryList.Where(e => address >= e.Key && address + len < e.Key + (ulong)e.Value.Size).FirstOrDefault(); 1364 if(keyValue.Value == null) 1365 { 1366 return false; 1367 } 1368 1369 var underlyingMemory = keyValue.Value; 1370 address -= keyValue.Key; 1371 1372 switch(accessWidth) 1373 { 1374 case AccessWidth._8bit: 1375 underlyingMemory.WriteByte((long)address, (byte)value); 1376 break; 1377 1378 case AccessWidth._16bit: 1379 underlyingMemory.WriteWord((long)address, (ushort)value); 1380 break; 1381 1382 case AccessWidth._20bit: 1383 underlyingMemory.WriteDoubleWord((long)address, value & GetAccessWidthMask(accessWidth)); 1384 break; 1385 1386 default: 1387 throw new Exception("unreachable"); 1388 } 1389 1390 return true; 1391 } 1392 PerformMemoryWrite(ulong address, uint value, AccessWidth accessWidth)1393 private void PerformMemoryWrite(ulong address, uint value, AccessWidth accessWidth) 1394 { 1395 if(machine.SystemBus.TryGetWatchpointsAt(address, Access.Write, out var _)) 1396 { 1397 pendingWatchpoints.Add(new PendingWatchpoint(address, accessWidth, value)); 1398 } 1399 1400 if(TryPerformDirectWrite(address, value, accessWidth)) 1401 { 1402 return; 1403 } 1404 1405 switch(accessWidth) 1406 { 1407 case AccessWidth._8bit: 1408 machine.SystemBus.WriteByte(address, (byte)value); 1409 break; 1410 1411 case AccessWidth._16bit: 1412 machine.SystemBus.WriteWord(address, (ushort)value); 1413 break; 1414 1415 case AccessWidth._20bit: 1416 machine.SystemBus.WriteWord(address, (ushort)value); 1417 machine.SystemBus.WriteWord(address + 2, (ushort)((value >> 16) & 0xF)); 1418 break; 1419 1420 default: 1421 throw new Exception("unreachable"); 1422 } 1423 } 1424 TryPerfrormDirectRead(ulong address, AccessWidth accessWidth, out uint value)1425 private bool TryPerfrormDirectRead(ulong address, AccessWidth accessWidth, out uint value) 1426 { 1427 var len = (ulong)GetAccessWidthInBytes(accessWidth); 1428 var keyValue = arrayMemoryList.Where(e => address >= e.Key && address + len < e.Key + (ulong)e.Value.Size).FirstOrDefault(); 1429 if(keyValue.Value == null) 1430 { 1431 value = 0; 1432 return false; 1433 } 1434 1435 var underlyingMemory = keyValue.Value; 1436 address -= keyValue.Key; 1437 1438 switch(accessWidth) 1439 { 1440 case AccessWidth._8bit: 1441 value = underlyingMemory.ReadByte((long)address); 1442 break; 1443 1444 case AccessWidth._16bit: 1445 value = underlyingMemory.ReadWord((long)address); 1446 break; 1447 1448 case AccessWidth._20bit: 1449 value = underlyingMemory.ReadDoubleWord((long)address); 1450 value &= GetAccessWidthMask(accessWidth); 1451 break; 1452 1453 default: 1454 throw new Exception("unreachable"); 1455 } 1456 1457 return true; 1458 } 1459 PerformMemoryRead(ulong address, AccessWidth accessWidth)1460 private uint PerformMemoryRead(ulong address, AccessWidth accessWidth) 1461 { 1462 if(machine.SystemBus.TryGetWatchpointsAt(address, Access.Read, out var _)) 1463 { 1464 pendingWatchpoints.Add(new PendingWatchpoint(address, accessWidth)); 1465 } 1466 1467 if(TryPerfrormDirectRead(address, accessWidth, out var directValue)) 1468 { 1469 return directValue; 1470 } 1471 1472 switch(accessWidth) 1473 { 1474 case AccessWidth._8bit: 1475 return machine.SystemBus.ReadByte(address); 1476 1477 case AccessWidth._16bit: 1478 return machine.SystemBus.ReadWord(address); 1479 1480 case AccessWidth._20bit: 1481 var value = (uint)machine.SystemBus.ReadWord(address); 1482 value |= (uint)(machine.SystemBus.ReadWord(address + 2) << 16); 1483 value &= GetAccessWidthMask(accessWidth); 1484 return value; 1485 1486 default: 1487 throw new Exception("unreachable"); 1488 } 1489 } 1490 GetAccessWidthInBits(AccessWidth accessWidth)1491 private static int GetAccessWidthInBits(AccessWidth accessWidth) 1492 { 1493 switch(accessWidth) 1494 { 1495 case AccessWidth._8bit: return 8; 1496 case AccessWidth._16bit: return 16; 1497 case AccessWidth._20bit: return 20; 1498 default: throw new Exception("unreachable"); 1499 } 1500 } 1501 GetAccessWidthInBytes(AccessWidth accessWidth)1502 private static int GetAccessWidthInBytes(AccessWidth accessWidth) 1503 { 1504 return (int)accessWidth; 1505 } 1506 GetAccessWidthMask(AccessWidth accessWidth)1507 private static uint GetAccessWidthMask(AccessWidth accessWidth) 1508 { 1509 return (1U << GetAccessWidthInBits(accessWidth)) - 1; 1510 } 1511 GetAccessWidthMSB(AccessWidth accessWidth)1512 private static uint GetAccessWidthMSB(AccessWidth accessWidth) 1513 { 1514 return (1U << (GetAccessWidthInBits(accessWidth) - 1)); 1515 } 1516 1517 private StatusFlags statusRegister; 1518 private ulong executedInstructions; 1519 1520 private readonly List<PendingWatchpoint> pendingWatchpoints = new List<PendingWatchpoint>(); 1521 private readonly SortedSet<int> pendingInterrupt = new SortedSet<int>(); 1522 private readonly IDictionary<ulong, HashSet<Action<ICpuSupportingGdb, ulong>>> hooks = 1523 new Dictionary<ulong, HashSet<Action<ICpuSupportingGdb, ulong>>>(); 1524 private readonly SortedList<ulong, ArrayMemory> arrayMemoryList = new SortedList<ulong, ArrayMemory>(); 1525 1526 private const uint InterruptVectorStart = 0xFFFE; 1527 1528 private sealed class PendingWatchpoint 1529 { PendingWatchpoint(ulong address, AccessWidth accessWidth, uint? value = null)1530 public PendingWatchpoint(ulong address, AccessWidth accessWidth, uint? value = null) 1531 { 1532 Address = address; 1533 AccessWidth = accessWidth; 1534 Value = value; 1535 } 1536 1537 public SysbusAccessWidth SysbusAccessWidth 1538 { 1539 get 1540 { 1541 switch(AccessWidth) 1542 { 1543 case AccessWidth._8bit: 1544 return SysbusAccessWidth.Byte; 1545 case AccessWidth._16bit: 1546 return SysbusAccessWidth.Word; 1547 case AccessWidth._20bit: 1548 return SysbusAccessWidth.DoubleWord; 1549 default: 1550 throw new Exception("unreachable"); 1551 } 1552 } 1553 } 1554 1555 public ulong Address { get; } 1556 public AccessWidth AccessWidth { get; } 1557 public uint? Value { get; } 1558 } 1559 1560 [Flags] 1561 private enum StatusFlags : ushort 1562 { 1563 Carry = (1 << 0), 1564 Zero = (1 << 1), 1565 Negative = (1 << 2), 1566 GeneralInterruptEnable = (1 << 3), 1567 CPUOff = (1 << 4), 1568 OscillatorOff = (1 << 5), 1569 SystemClockGenerator0 = (1 << 6), 1570 SystemClockGenerator1 = (1 << 7), 1571 Overflow = (1 << 8) 1572 } 1573 1574 // NOTE: Enum value stores a byte width 1575 private enum AccessWidth 1576 { 1577 _8bit = 1, 1578 _16bit = 2, 1579 _20bit = 4, 1580 } 1581 1582 private enum AddressingMode 1583 { 1584 Register, 1585 Indexed, 1586 IndirectRegister, 1587 IndirectAutoincrement, 1588 } 1589 1590 public enum Registers : int 1591 { 1592 PC, 1593 SP, 1594 SR, 1595 R3, 1596 R4, 1597 R5, 1598 R6, 1599 R7, 1600 R8, 1601 R9, 1602 R10, 1603 R11, 1604 R12, 1605 R13, 1606 R14, 1607 R15, 1608 } 1609 } 1610 } 1611