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