1 using System;
2 using System.Collections;
3
4 namespace cmsx.debug
5 {
6 public enum State
7 {
8 running, exit
9 }
10
11 public enum CommandResult
12 {
13 ret, cont, update
14 }
15
16 class ByStart : Rel<cmsx.object.Symbol*>
17 {
18 public bool operator()(cmsx.object.Symbol* left, cmsx.object.Symbol* right) const
19 {
20 return left->start < right->start;
21 }
22 }
23
24 public const short blackOnWhite = 1;
25 public const short whiteOnBlue = 2;
26 public const short cyanOnBlue = 3;
27 public const short whiteOnRed = 4;
28 public const short redOnWhite = 5;
29 public const short whiteOnCyan = 6;
30
31 public class Screen
32 {
33 public nothrow Screen()
34 {
35 RtInitScreen();
36 RtNoEcho();
37 RtRaw();
38 RtKeyPad();
39 GetDimensions();
40 RtStartColor();
41 RtInitPair(blackOnWhite, RtBlack(), RtWhite());
42 RtInitPair(whiteOnBlue, RtWhite(), RtBlue());
43 RtInitPair(cyanOnBlue, RtCyan(), RtBlue());
44 RtInitPair(whiteOnRed, RtWhite(), RtRed());
45 RtInitPair(redOnWhite, RtRed(), RtWhite());
46 RtInitPair(whiteOnCyan, RtWhite(), RtCyan());
47 }
48 public ~Screen()
49 {
50 RtDoneScreen();
51 }
52 public nothrow void MoveCursorTo(int row, int col)
53 {
54 RtMove(row, col);
55 }
56 public nothrow void Update()
57 {
58 Window* window = focusedWindow->next;
59 while (window != focusedWindow)
60 {
61 if (window->visible)
62 {
63 window->Draw();
64 }
65 window = window->next;
66 }
67 focusedWindow->Draw();
68 focusedWindow->ShowCursor();
69 RtRefresh();
70 }
71 public nothrow void GetDimensions()
72 {
73 RtGetMaxYX(&rows, &cols);
74 commandWindowHeight = 3;
75 outputWindowHeight = 10;
76 }
77 public int rows;
78 public int cols;
79 public int outputWindowHeight;
80 public int commandWindowHeight;
81 public Window* focusedWindow;
82 }
83
84 public static class Box
85 {
86 static Box() : chars(), doubleChars()
87 {
88 chars.Append(cast<uchar>(0x250C));
89 chars.Append(cast<uchar>(0x2500));
90 chars.Append(cast<uchar>(0x2502));
91 chars.Append(cast<uchar>(0x2510));
92 chars.Append(cast<uchar>(0x2514));
93 chars.Append(cast<uchar>(0x2518));
94 doubleChars.Append(cast<uchar>(0x2554));
95 doubleChars.Append(cast<uchar>(0x2550));
96 doubleChars.Append(cast<uchar>(0x2551));
97 doubleChars.Append(cast<uchar>(0x2557));
98 doubleChars.Append(cast<uchar>(0x255A));
99 doubleChars.Append(cast<uchar>(0x255D));
100 }
101 public static ustring* GetChars()
102 {
103 return &chars;
104 }
105 public static ustring* GetDoubleChars()
106 {
107 return &doubleChars;
108 }
109 public static ustring chars;
110 public static ustring doubleChars;
111 }
112
113 public abstract class Window
114 {
115 public nothrow Window(const string& name_) : name(name_)
116 {
117 }
118 public virtual default ~Window();
119 public void SetDebugger(Debugger* debugger_)
120 {
121 debugger = debugger_;
122 }
123 public abstract void Draw();
124 public void DrawFrame()
125 {
126 ustring* frameChars = null;
127 if (screen->focusedWindow == this)
128 {
129 frameChars = Box.GetDoubleChars();
130 }
131 else
132 {
133 frameChars = Box.GetChars();
134 }
135 RtAttrOn(RtColorPair(color));
136 screen->MoveCursorTo(Top(), Left());
137 int leftTop = cast<int>((*frameChars)[0]);
138 RtAddCh(leftTop);
139 int horizontal = cast<int>((*frameChars)[1]);
140 for (int i = Left() + 1; i < Left() + Width() - 1; ++i;)
141 {
142 screen->MoveCursorTo(Top(), i);
143 RtAddCh(horizontal);
144 screen->MoveCursorTo(Top() + Height() - 1, i);
145 RtAddCh(horizontal);
146 }
147 int rightTop = cast<int>((*frameChars)[3]);
148 screen->MoveCursorTo(Top(), Left() + Width() - 1);
149 RtAddCh(rightTop);
150 int vertical = cast<int>((*frameChars)[2]);
151 for (int i = Top() + 1; i < Top() + Height() - 1; ++i;)
152 {
153 screen->MoveCursorTo(i, Left());
154 RtAddCh(vertical);
155 screen->MoveCursorTo(i, Left() + Width() - 1);
156 RtAddCh(vertical);
157 }
158 int leftBottom = cast<int>((*frameChars)[4]);
159 screen->MoveCursorTo(Top() + Height() - 1, Left());
160 RtAddCh(leftBottom);
161 int rightBottom = cast<int>((*frameChars)[5]);
162 screen->MoveCursorTo(Top() + Height() - 1, Left() + Width() - 1);
163 RtAddCh(rightBottom);
164 RtAttrOff(RtColorPair(color));
165 if (!name.IsEmpty())
166 {
167 RtAttrOn(RtColorPair(color));
168 screen->MoveCursorTo(Top(), Left() + 2);
169 string s;
170 s.Append(' ');
171 s.Append(name);
172 s.Append(' ');
173 for (char c : s)
174 {
175 int ch = cast<int>(c);
176 RtAddCh(ch);
177 }
178 RtAttrOff(RtColorPair(color));
179 }
180 }
181 public abstract CommandResult HandleKey(int key);
182 public int Top()
183 {
184 if (zoomed)
185 {
186 return 1;
187 }
188 else
189 {
190 return top;
191 }
192 }
193 public int Height()
194 {
195 if (zoomed)
196 {
197 return screen->rows;
198 }
199 else
200 {
201 return height;
202 }
203 }
204 public int Left()
205 {
206 if (zoomed)
207 {
208 return 0;
209 }
210 else
211 {
212 return left;
213 }
214 }
215 public int Width()
216 {
217 if (zoomed)
218 {
219 return screen->cols;
220 }
221 else
222 {
223 return width;
224 }
225 }
226 public void ShowCursor()
227 {
228 screen->MoveCursorTo(cursorRow, cursorCol);
229 }
230 public string name;
231 public Screen* screen;
232 public Debugger* debugger;
233 public short color;
234 public bool visible;
235 public bool zoomed;
236 public int top;
237 public int height;
238 public int left;
239 public int width;
240 public int cursorRow;
241 public int cursorCol;
242 public Window* next;
243 }
244
245 public const uint stoiFpSpInst = (cmsx.machine.STOI << 24u) | (cmsx.machine.regFP << 16u) | (cmsx.machine.regSP << 8u) | (0u << 0u);
246 public const uint oriFpSpInst = (cmsx.machine.ORI << 24u) | (cmsx.machine.regFP << 16u) | (cmsx.machine.regSP << 8u) | (0u << 0u);
247 public const uint retInst = (cmsx.machine.RET << 24u) | (0u << 16u) | (0u << 8u) | (0u << 0u);
248
249 public nothrow bool InProlog(uint currentInst)
250 {
251 return currentInst == stoiFpSpInst || currentInst == oriFpSpInst;
252 }
253
254 public nothrow bool InEpilog(uint currentInst)
255 {
256 return currentInst == retInst;
257 }
258
259 public uint ReadInst(cmsx.kernel.Process* process, ulong address)
260 {
261 ulong inst = 0u;
262 try
263 {
264 cmsx.kernel.ReadProcessMemory(cmsx.machine.GetMachine(), process, address, inst, 4u, false);
265 }
266 catch (Exception& ex)
267 {
268 cmsx.machine.Panic("debugger could not read process memory: " + ex.ToString());
269 }
270 return cast<uint>(inst);
271 }
272
273 public void WriteInst(cmsx.kernel.Process* process, ulong address, uint inst)
274 {
275 try
276 {
277 cmsx.kernel.WriteProcessMemory(cmsx.machine.GetMachine(), process, address, inst, 4u, cmsx.machine.Protection.execute);
278 }
279 catch (Exception& ex)
280 {
281 cmsx.machine.Panic("debugger could not writer to process memory: " + ex.ToString());
282 }
283 }
284
285 public class Breakpoint
286 {
287 public enum Kind
288 {
289 step, hard
290 }
291 public nothrow Breakpoint(Kind kind_, ulong address_, uint inst_) : kind(kind_), address(address_), inst(inst_), fp(0u)
292 {
293 }
294 public Kind kind;
295 public ulong address;
296 public uint inst;
297 public ulong fp;
298 }
299
300 public class Breakpoints
301 {
302 public nothrow Breakpoint* GetBreakpoint(ulong address) const
303 {
304 HashMap<ulong, Breakpoint>.ConstIterator it = breakpoints.CFind(address);
305 if (it != breakpoints.CEnd())
306 {
307 return &it->second;
308 }
309 else
310 {
311 return null;
312 }
313 }
314 public nothrow void SetBreakpoint(const Breakpoint& bp)
315 {
316 breakpoints[bp.address] = bp;
317 }
318 public nothrow void RemoveBreakpoint(ulong address)
319 {
320 breakpoints.Remove(address);
321 }
322 private HashMap<ulong, Breakpoint> breakpoints;
323 }
324
325 public class HeaderLine
326 {
327 public nothrow HeaderLine() : header()
328 {
329 }
330 public void SetHeader(const string& header_)
331 {
332 header = header_;
333 }
334 public void Draw(Screen* screen)
335 {
336 int col = 0;
337 bool hilite = false;
338 RtAttrOn(RtColorPair(blackOnWhite));
339 for (char c : header)
340 {
341 if (c == '*')
342 {
343 if (!hilite)
344 {
345 RtAttrOff(RtColorPair(blackOnWhite));
346 RtAttrOn(RtColorPair(redOnWhite));
347 hilite = true;
348 }
349 else
350 {
351 RtAttrOff(RtColorPair(redOnWhite));
352 RtAttrOn(RtColorPair(blackOnWhite));
353 hilite = false;
354 }
355 }
356 else
357 {
358 int ch = cast<int>(c);
359 screen->MoveCursorTo(0, col);
360 RtAddCh(ch);
361 ++col;
362 }
363 }
364 for (int i = col; i < screen->cols; ++i;)
365 {
366 int ch = cast<int>(' ');
367 screen->MoveCursorTo(0, i);
368 RtAddCh(ch);
369 }
370 RtAttrOff(RtColorPair(blackOnWhite));
371 }
372 private string header;
373 }
374
375 public class DissamblyFormatter : cmsx.machine.Formatter
376 {
377 public nothrow DissamblyFormatter() : symbolTable(null)
378 {
379 }
380 public nothrow void SetSymbolTable(cmsx.object.SymbolTable* symbolTable_)
381 {
382 symbolTable = symbolTable_;
383 }
384 public nothrow void SetBreakpoints(Breakpoints* breakpoints_)
385 {
386 breakpoints = breakpoints_;
387 }
388 public nothrow void SetPC(ulong pc)
389 {
390 this->pc = pc;
391 }
392 public override string FormatRegisterNumber(byte x)
393 {
394 cmsx.object.Symbol* registerSymbol = symbolTable->GetRegisterSymbol(x);
395 if (registerSymbol != null)
396 {
397 return registerSymbol->name;
398 }
399 else
400 {
401 return base->FormatRegisterNumber(x);
402 }
403 }
404 public override string GetLabel(ulong address)
405 {
406 cmsx.object.Symbol* symbol = symbolTable->GetSymbolByAddress(address);
407 if (symbol != null)
408 {
409 if (!symbol->localName.IsEmpty())
410 {
411 return symbol->localName;
412 }
413 else
414 {
415 return symbol->name;
416 }
417 }
418 else
419 {
420 return string();
421 }
422 }
423 public override string GetTags(ulong address)
424 {
425 string tags;
426 if (breakpoints->GetBreakpoint(address) != null)
427 {
428 tags = tags + "*";
429 }
430 else
431 {
432 tags = tags + " ";
433 }
434 if (address == pc)
435 {
436 tags = tags + ">";
437 }
438 else
439 {
440 tags = tags + " ";
441 }
442 tags = tags + " ";
443 return tags;
444 }
445 private cmsx.object.SymbolTable* symbolTable;
446 private ulong pc;
447 private Breakpoints* breakpoints;
448 }
449
450 public class Disassembly : Window
451 {
452 public nothrow Disassembly(Breakpoints* breakpoints_) : base("Disassembly"), disassemblyFormatter(), breakpoints(breakpoints_)
453 {
454 header.SetHeader("*ESC* Exit *F1* Help *F2* Enter Command *F4* Output *F5* Run *F6* Memory *F8* Focus Next *F9* Toggle Breakpoint *F10* Step Over *F11* Step Into *F12* Step Out");
455 }
456 public nothrow void SetSymbolTable(cmsx.object.SymbolTable* symbolTable)
457 {
458 disassemblyFormatter.SetSymbolTable(symbolTable);
459 }
460 public nothrow void SetBreakpoints(Breakpoints* breakpoints)
461 {
462 disassemblyFormatter.SetBreakpoints(breakpoints);
463 }
464 public nothrow void SetScreen(Screen* screen_)
465 {
466 screen = screen_;
467 color = whiteOnBlue;
468 top = 1;
469 left = 0;
470 height = screen->rows - top - screen->commandWindowHeight - screen->outputWindowHeight;
471 width = screen->cols - left;
472 cursorRow = top;
473 cursorCol = left;
474 }
475 public override CommandResult HandleKey(int key)
476 {
477 switch (key)
478 {
479 case keyDown:
480 {
481 debugger->middle = debugger->middle + 4u;
482 return CommandResult.update;
483 }
484 case keyUp:
485 {
486 if (debugger->start >= 4u)
487 {
488 debugger->middle = debugger->middle - 4u;
489 }
490 return CommandResult.update;
491 }
492 case keyPgDown:
493 {
494 debugger->middle = debugger->middle + 4u * debugger->numInstsInWindow;
495 return CommandResult.update;
496 }
497 case keyPgUp:
498 {
499 if (debugger->start >= 4 * debugger->numInstsInWindow)
500 {
501 debugger->middle = debugger->middle - 4u * debugger->numInstsInWindow;
502 }
503 return CommandResult.update;
504 }
505 case keyHome:
506 {
507 debugger->middle = debugger->pc;
508 return CommandResult.update;
509 }
510 case keyF5:
511 {
512 debugger->Run();
513 return CommandResult.ret;
514 }
515 case keyF9:
516 {
517 debugger->ToggleBreakpoint(debugger->middle);
518 return CommandResult.update;
519 }
520 case keyF10:
521 {
522 debugger->StepOver();
523 return CommandResult.ret;
524 }
525 case keyF11:
526 {
527 debugger->StepInto();
528 return CommandResult.ret;
529 }
530 case keyF12:
531 {
532 debugger->StepOut();
533 return CommandResult.ret;
534 }
535 }
536 return CommandResult.cont;
537 }
538 public void RetrieveInstructions(ulong pc, cmsx.kernel.Process* process)
539 {
540 ulong numInstsInWindow = cast<ulong>(Height() - 2);
541 ulong start = pc - 4u * (numInstsInWindow / 2u);
542 ulong end = start + (4u * numInstsInWindow);
543 for (ulong address = start; address < end; address = address + 4u;)
544 {
545 HashMap<ulong, long>.ConstIterator it = instructions.CFind(address);
546 if (it == instructions.CEnd())
547 {
548 if (address < entryPointAddress || address >= instructionEndAddress)
549 {
550 instructions[address] = -1;
551 }
552 else
553 {
554 uint inst = ReadInst(process, address);
555 Breakpoint* bp = breakpoints->GetBreakpoint(address);
556 if (bp != null)
557 {
558 inst = bp->inst;
559 }
560 instructions[address] = cast<long>(inst);
561 }
562 }
563 }
564 }
565 public override void Draw()
566 {
567 header.Draw(screen);
568 DrawFrame();
569 disassemblyFormatter.SetPC(debugger->pc);
570 RetrieveInstructions(debugger->middle, debugger->process);
571 ulong numInstsInWindow = cast<ulong>(Height() - 2);
572 ulong start = debugger->middle - 4u * (numInstsInWindow / 2u);
573 ulong end = start + (4u * numInstsInWindow);
574 int row = Top() + 1;
575 cursorRow = row;
576 cursorCol = Left() + 1;
577 for (ulong address = start; address < end; address = address + 4u;)
578 {
579 screen->MoveCursorTo(row, Left() + 1);
580 ustring line = GetInstructionLine(address);
581 if (address == debugger->middle)
582 {
583 if (breakpoints->GetBreakpoint(address) != null)
584 {
585 RtAttrOn(RtColorPair(redOnWhite));
586 }
587 else
588 {
589 RtAttrOn(RtColorPair(blackOnWhite));
590 }
591 }
592 else if (breakpoints->GetBreakpoint(address) != null)
593 {
594 RtAttrOn(RtColorPair(whiteOnRed));
595 }
596 else if (address == debugger->pc)
597 {
598 RtAttrOn(RtColorPair(cyanOnBlue));
599 }
600 else
601 {
602 RtAttrOn(RtColorPair(color));
603 }
604 for (int col = Left() + 1; col < Left() + Width() - 1; ++col;)
605 {
606 int ch = cast<int>(' ');
607 int charIndex = col - (Left() + 1);
608 if (charIndex < line.Length())
609 {
610 ch = cast<int>(line[charIndex]);
611 }
612 RtAddCh(ch);
613 }
614 if (address == debugger->middle)
615 {
616 if (breakpoints->GetBreakpoint(address) != null)
617 {
618 RtAttrOff(RtColorPair(redOnWhite));
619 }
620 else
621 {
622 RtAttrOff(RtColorPair(blackOnWhite));
623 }
624 cursorRow = row;
625 }
626 else if (breakpoints->GetBreakpoint(address) != null)
627 {
628 RtAttrOff(RtColorPair(whiteOnRed));
629 }
630 else if (address == debugger->pc)
631 {
632 RtAttrOff(RtColorPair(cyanOnBlue));
633 }
634 else
635 {
636 RtAttrOff(RtColorPair(color));
637 }
638 ++row;
639 }
640 if (screen->focusedWindow == this)
641 {
642 screen->MoveCursorTo(cursorRow, cursorCol);
643 }
644 }
645 public nothrow void SetEntryPointAddress(ulong entryPointAddress_)
646 {
647 entryPointAddress = entryPointAddress_;
648 }
649 public nothrow void SetInstructionEndAddress(ulong instructionEndAddress_)
650 {
651 instructionEndAddress = instructionEndAddress_;
652 }
653 private ustring GetInstructionLine(ulong address)
654 {
655 HashMap<ulong, long>.ConstIterator it = instructions.CFind(address);
656 long instruction = -1;
657 if (it != instructions.CEnd())
658 {
659 instruction = it->second;
660 }
661 if (instruction == -1)
662 {
663 string result = "#" + ToHexString(address);
664 return ToUtf32(result);
665 }
666 uint inst = cast<uint>(instruction);
667 byte opCode = cast<byte>(inst >> 24u);
668 byte x = cast<byte>(inst >> 16u);
669 byte y = cast<byte>(inst >> 8u);
670 byte z = cast<byte>(inst);
671 string instructionLine = cmsx.machine.FormatInstruction(address, opCode, x, y, z, disassemblyFormatter);
672 return ToUtf32(instructionLine);
673 }
674 private HeaderLine header;
675 private Breakpoints* breakpoints;
676 private DissamblyFormatter disassemblyFormatter;
677 private HashMap<ulong, long> instructions;
678 private ulong entryPointAddress;
679 private ulong instructionEndAddress;
680 }
681
682 public class Memory : Window
683 {
684 public enum Format
685 {
686 bytes, wydes, tetras, octas
687 }
688 public nothrow Memory() :
689 base("Memory - data segment"), header(), segmentIndex(cmsx.machine.dataSegmentIndex), start(0u), end(0u), bufferStartAddress(0u), topAddress(0u), numBytesInRow(0), init(true),
690 format(Format.bytes)
691 {
692 header.SetHeader("*ESC* Exit *F1* Help *F2* Enter Command *F3* Disassembly *F4* Output *F8* Focus Next *D* Data Segment *P* Pool Segment *S* Stack Segment *B* Bytes *W* Wydes *T* Tetras *O* Octas");
693 }
694 public nothrow void SetScreen(Screen* screen_)
695 {
696 screen = screen_;
697 color = whiteOnBlue;
698 top = 1;
699 left = 0;
700 height = screen->rows - top - screen->commandWindowHeight - screen->outputWindowHeight;
701 width = screen->cols - left;
702 cursorRow = top + 1;
703 cursorCol = left + 1;
704 numBytesInRow = 16;
705 }
706 public override void Draw()
707 {
708 header.Draw(screen);
709 DrawFrame();
710 RtAttrOn(RtColorPair(color));
711 if (init)
712 {
713 ResolveLimits(debugger->process);
714 init = false;
715 }
716 int n = Height() - 2;
717 for (int i = 0; i < n; ++i;)
718 {
719 int row = Top() + 1 + i;
720 int col = Left() + 1;
721 screen->MoveCursorTo(row, col);
722 ulong addr = topAddress + cast<ulong>(i * numBytesInRow);
723 string line = GetLine(addr);
724 for (int j = 0; j < Width() - 2; ++j;)
725 {
726 int ch = cast<int>(' ');
727 if (j < line.Length())
728 {
729 ch = cast<int>(line[j]);
730 }
731 RtAddCh(ch);
732 }
733 }
734 RtAttrOff(RtColorPair(color));
735 }
736 public void ShowAddress(byte segment, ulong address)
737 {
738 segmentIndex = segment;
739 topAddress = address;
740 init = true;
741 }
742 public override CommandResult HandleKey(int key)
743 {
744 switch (key)
745 {
746 case keyHome: GoStart(); return CommandResult.update;
747 case keyEnd: GoEnd(); return CommandResult.update;
748 case keyUp: GoUp(); return CommandResult.update;
749 case keyDown: GoDown(); return CommandResult.update;
750 case keyPgUp: GoPageUp(); return CommandResult.update;
751 case keyPgDown: GoPageDown(); return CommandResult.update;
752 case cast<int>('d'): segmentIndex = cmsx.machine.dataSegmentIndex; name = "Memory - data segment"; init = true; return CommandResult.update;
753 case cast<int>('p'): segmentIndex = cmsx.machine.poolSegmentIndex; name = "Memory - pool segment"; init = true; return CommandResult.update;
754 case cast<int>('s'): segmentIndex = cmsx.machine.stackSegmentIndex; name = "Memory - stack segment"; init = true; return CommandResult.update;
755 case cast<int>('b'): format = Format.bytes; init = true; return CommandResult.update;
756 case cast<int>('w'): format = Format.wydes; init = true; return CommandResult.update;
757 case cast<int>('t'): format = Format.tetras; init = true; return CommandResult.update;
758 case cast<int>('o'): format = Format.octas; init = true; return CommandResult.update;
759 }
760 return CommandResult.cont;
761 }
762 private long NumBytesInWindow()
763 {
764 long numBytesInWindow = (cast<long>(Height()) - 2) * numBytesInRow;
765 return numBytesInWindow;
766 }
767 private void GoStart()
768 {
769 topAddress = start;
770 }
771 private void GoEnd()
772 {
773 long addr = cast<long>(end);
774 addr = addr - NumBytesInWindow();
775 if (addr < cast<long>(start))
776 {
777 addr = cast<long>(start);
778 }
779 topAddress = cast<ulong>(addr);
780 }
781 private void GoUp()
782 {
783 topAddress = Max(start, topAddress - cast<ulong>(numBytesInRow));
784 }
785 private void GoDown()
786 {
787 topAddress = topAddress + cast<ulong>(numBytesInRow);
788 if (topAddress + cast<ulong>(NumBytesInWindow()) >= end)
789 {
790 GoEnd();
791 }
792 }
793 private void GoPageUp()
794 {
795 long addr = cast<long>(topAddress);
796 addr = addr - NumBytesInWindow();
797 if (addr < cast<long>(start))
798 {
799 addr = cast<long>(start);
800 }
801 topAddress = cast<ulong>(addr);
802 }
803 private void GoPageDown()
804 {
805 long numBytesInWindow = NumBytesInWindow();
806 long addr = cast<long>(topAddress);
807 addr = addr + numBytesInWindow;
808 if (addr + numBytesInWindow >= cast<long>(end))
809 {
810 GoEnd();
811 }
812 else
813 {
814 topAddress = cast<ulong>(addr);
815 }
816 }
817 private void ResolveLimits(cmsx.kernel.Process* process)
818 {
819 cmsx.kernel.SegmentDescriptor* segmentDescriptor = process->memoryTable.segmentDescriptors[segmentIndex];
820 start = segmentDescriptor->baseAddress + segmentDescriptor->startAddress;
821 end = start + segmentDescriptor->length;
822 topAddress = start;
823 bufferStartAddress = 0u;
824 }
825 private string GetLine(ulong addr)
826 {
827 if (addr >= end)
828 {
829 return string();
830 }
831 string line = "#";
832 line.Append(ToHexString(addr));
833 if (format == Format.bytes)
834 {
835 string chars;
836 for (int j = 0; j < numBytesInRow; ++j;)
837 {
838 if (j == numBytesInRow / 2)
839 {
840 line.Append(" -");
841 }
842 int x = GetByte(addr + cast<ulong>(j));
843 if (x != -1)
844 {
845 byte b = cast<byte>(x);
846 line.Append(' ').Append(ToHexString(b));
847 char c = cast<char>(b);
848 if (!IsPrintable(c))
849 {
850 c = '.';
851 }
852 chars.Append(c);
853 }
854 else
855 {
856 line.Append(" ");
857 chars.Append(' ');
858 }
859 }
860 line.Append(' ');
861 line.Append(chars);
862 return line;
863 }
864 else if (format == Format.wydes)
865 {
866 for (int j = 0; j < numBytesInRow / 2; ++j;)
867 {
868 if (j == numBytesInRow / 4)
869 {
870 line.Append(" -");
871 }
872 int x = GetWyde(addr + cast<ulong>(j * 2));
873 if (x != -1)
874 {
875 ushort w = cast<ushort>(x);
876 line.Append(' ').Append(ToHexString(w));
877 }
878 else
879 {
880 line.Append(' ', 4 + 1);
881 }
882 }
883 return line;
884 }
885 else if (format == Format.tetras)
886 {
887 for (int j = 0; j < numBytesInRow / 4; ++j;)
888 {
889 if (j == numBytesInRow / 8)
890 {
891 line.Append(" -");
892 }
893 long x = GetTetra(addr + cast<ulong>(j * 4));
894 if (x != -1)
895 {
896 uint i = cast<uint>(x);
897 line.Append(' ').Append(ToHexString(i));
898 }
899 else
900 {
901 line.Append(' ', 8 + 1);
902 }
903 }
904 return line;
905 }
906 else if (format == Format.octas)
907 {
908 for (int j = 0; j < numBytesInRow / 8; ++j;)
909 {
910 if (j == numBytesInRow / 16)
911 {
912 line.Append(" -");
913 }
914 Pair<ulong, bool> x = GetOcta(addr + cast<ulong>(j * 8));
915 if (x.second)
916 {
917 line.Append(' ').Append(ToHexString(x.first));
918 }
919 else
920 {
921 line.Append(' ', 16 + 1);
922 }
923 }
924 return line;
925 }
926 else
927 {
928 return string();
929 }
930 }
931 private int GetByte(ulong addr)
932 {
933 bool fillBuf = false;
934 if (bufferStartAddress == 0u)
935 {
936 bufferStartAddress = start;
937 fillBuf = true;
938 }
939 while (bufferStartAddress >= start && addr < bufferStartAddress)
940 {
941 bufferStartAddress = bufferStartAddress - cast<ulong>(buffer.Length());
942 fillBuf = true;
943 }
944 while (bufferStartAddress + cast<ulong>(buffer.Length()) < end && addr >= bufferStartAddress + cast<ulong>(buffer.Length()))
945 {
946 bufferStartAddress = bufferStartAddress + cast<ulong>(buffer.Length());
947 fillBuf = true;
948 }
949 if (fillBuf)
950 {
951 FillBuffer();
952 }
953 if (addr >= bufferStartAddress && addr < bufferStartAddress + cast<ulong>(buffer.Length()))
954 {
955 return buffer[cast<long>(addr - bufferStartAddress)];
956 }
957 return -1;
958 }
959 private int GetWyde(ulong addr)
960 {
961 int b1 = GetByte(addr);
962 int b2 = GetByte(addr + 1u);
963 if (b1 != -1 && b2 != -1)
964 {
965 return cast<ushort>(b1) << 8u | cast<ushort>(b2);
966 }
967 else
968 {
969 return -1;
970 }
971 }
972 private long GetTetra(ulong addr)
973 {
974 int b1 = GetByte(addr);
975 int b2 = GetByte(addr + 1u);
976 int b3 = GetByte(addr + 2u);
977 int b4 = GetByte(addr + 3u);
978 if (b1 != -1 && b2 != -1 && b3 != -1 && b4 != -1)
979 {
980 return cast<uint>(b1) << 24u | cast<uint>(b2) << 16u | cast<uint>(b3) << 8u | cast<uint>(b4);
981 }
982 else
983 {
984 return -1;
985 }
986 }
987 private Pair<ulong, bool> GetOcta(ulong addr)
988 {
989 int b1 = GetByte(addr);
990 int b2 = GetByte(addr + 1u);
991 int b3 = GetByte(addr + 2u);
992 int b4 = GetByte(addr + 3u);
993 int b5 = GetByte(addr + 4u);
994 int b6 = GetByte(addr + 5u);
995 int b7 = GetByte(addr + 6u);
996 int b8 = GetByte(addr + 7u);
997 if (b1 != -1 && b2 != -1 && b3 != -1 && b4 != -1 && b5 != -1 && b6 != -1 && b7 != -1 && b8 != -1)
998 {
999 return MakePair(
1000 cast<ulong>(b1) << 56u | cast<ulong>(b2) << 48u | cast<ulong>(b3) << 40u | cast<ulong>(b4) << 32u | cast<ulong>(b5) << 24u | cast<ulong>(b6) << 16u | cast<ulong>(b7) << 8u | cast<ulong>(b8),
1001 true);
1002 }
1003 else
1004 {
1005 return MakePair(cast<ulong>(0u), false);
1006 }
1007 }
1008 private void FillBuffer()
1009 {
1010 ulong size = Min(end - bufferStartAddress, cast<ulong>(buffer.Length()));
1011 try
1012 {
1013 cmsx.kernel.ReadProcessMemory(*debugger->machine, debugger->process, bufferStartAddress, &buffer[0], size, false);
1014 }
1015 catch (const Exception& ex)
1016 {
1017 cmsx.machine.Panic("debugger: could not read process memory: " + ex.ToString());
1018 }
1019 }
1020 private HeaderLine header;
1021 private byte segmentIndex;
1022 private ulong start;
1023 private ulong end;
1024 private ulong topAddress;
1025 private byte[4096] buffer;
1026 private ulong bufferStartAddress;
1027 private int numBytesInRow;
1028 private bool init;
1029 private Format format;
1030 }
1031
1032 public class Output : Window
1033 {
1034 public nothrow Output() : base("Output")
1035 {
1036 header.SetHeader("*ESC* Exit *F1* Help *F2* Enter Command *F3* Disassembly *F6* Memory *F8* Focus Next *C* Clear");
1037 }
1038 public void SetScreen(Screen* screen_)
1039 {
1040 screen = screen_;
1041 color = whiteOnCyan;
1042 top = screen->rows - screen->commandWindowHeight - screen->outputWindowHeight;
1043 left = 0;
1044 height = screen->outputWindowHeight;
1045 width = screen->cols - left;
1046 cursorRow = top + height - 2;
1047 cursorCol = left;
1048 topLineIndex = 0;
1049 }
1050 public override void Draw()
1051 {
1052 header.Draw(screen);
1053 DrawFrame();
1054 int n = Top() + Height() - 1;
1055 cursorCol = Left() + 1;
1056 RtAttrOn(RtColorPair(color));
1057 for (int i = Top() + 1; i < n; ++i;)
1058 {
1059 int lineIndex = topLineIndex + i - (Top() + 1);
1060 ustring line;
1061 if (lineIndex >= 0 && lineIndex < lines.Count())
1062 {
1063 line = lines[lineIndex].first;
1064 }
1065 int m = Width() - 1;
1066 for (int j = Left() + 1; j < m; ++j;)
1067 {
1068 screen->MoveCursorTo(i, j);
1069 int charIndex = j - (Left() + 1);
1070 int ch = cast<int>(' ');
1071 if (charIndex >= 0 && charIndex < line.Length())
1072 {
1073 uchar c = line[charIndex];
1074 if (IsGraphic(c))
1075 {
1076 ch = cast<int>(c);
1077 }
1078 }
1079 RtAddCh(ch);
1080 }
1081 }
1082 RtAttrOff(RtColorPair(color));
1083 if (screen->focusedWindow == this)
1084 {
1085 screen->MoveCursorTo(cursorRow, cursorCol);
1086 }
1087 }
1088 public override CommandResult HandleKey(int key)
1089 {
1090 switch (key)
1091 {
1092 case keyEnter:
1093 {
1094 int lineIndex = topLineIndex + cursorRow - (Top() + 1);
1095 if (lineIndex >= 0 && lineIndex < lines.Count())
1096 {
1097 ulong address = lines[lineIndex].second;
1098 if (address != 0u)
1099 {
1100 debugger->ShowAddress(address);
1101 return CommandResult.update;
1102 }
1103 }
1104 break;
1105 }
1106 case keyUp:
1107 {
1108 if (cursorRow > Top() + 1)
1109 {
1110 --cursorRow;
1111 return CommandResult.update;
1112 }
1113 else if (topLineIndex > 0)
1114 {
1115 topLineIndex = topLineIndex - 1;
1116 return CommandResult.update;
1117 }
1118 break;
1119 }
1120 case keyDown:
1121 {
1122 if (cursorRow < Top() + Height() - 2)
1123 {
1124 ++cursorRow;
1125 return CommandResult.update;
1126 }
1127 else
1128 {
1129 int n = Height() - 2;
1130 if (topLineIndex < lines.Count() - n)
1131 {
1132 topLineIndex = topLineIndex + 1;
1133 return CommandResult.update;
1134 }
1135 }
1136 break;
1137 }
1138 case cast<int>('c'):
1139 {
1140 lines.Clear();
1141 int n = Height() - 2;
1142 topLineIndex = cast<int>(lines.Count() - n);
1143 cursorRow = Top() + Height() - 2;
1144 return CommandResult.update;
1145 }
1146 }
1147 return CommandResult.cont;
1148 }
1149 public void AddChar(uchar c, ulong address)
1150 {
1151 if (lines.IsEmpty())
1152 {
1153 lines.Add(MakePair(ustring(), cast<ulong>(0u)));
1154 }
1155 if (IsGraphic(c))
1156 {
1157 lines.Back().first.Append(c);
1158 lines.Back().second = address;
1159 }
1160 else
1161 {
1162 if (c == '\n')
1163 {
1164 lines.Add(MakePair(ustring(), cast<ulong>(0u)));
1165 }
1166 else if (cast<int>(c) < 128)
1167 {
1168 lines.Back().first.Append(ToUtf32(MakeCharLiteral(cast<char>(c))));
1169 lines.Back().second = address;
1170 }
1171 }
1172 }
1173 public void AddLine(const ustring& line, ulong address)
1174 {
1175 for (uchar c : line)
1176 {
1177 AddChar(c, address);
1178 }
1179 if (!lines.IsEmpty())
1180 {
1181 if (!lines.Back().first.IsEmpty())
1182 {
1183 lines.Add(MakePair(ustring(), cast<ulong>(0u)));
1184 }
1185 }
1186 int n = Height() - 2;
1187 topLineIndex = cast<int>(lines.Count() - n);
1188 cursorRow = Top() + Height() - 2;
1189 }
1190 private HeaderLine header;
1191 private List<Pair<ustring, ulong>> lines;
1192 private int topLineIndex;
1193 }
1194
1195 public class CommandWindow : Window
1196 {
1197 public nothrow CommandWindow() : base("Command")
1198 {
1199 header.SetHeader("*ESC* Exit *F1* Help *F3* Disassembly *F4* Output *F6* Memory *F8* Focus Next");
1200 }
1201 public void SetScreen(Screen* screen_)
1202 {
1203 screen = screen_;
1204 color = blackOnWhite;
1205 top = screen->rows - screen->commandWindowHeight;
1206 left = 0;
1207 height = screen->commandWindowHeight;
1208 width = screen->cols - left;
1209 cursorRow = top + height - 2;
1210 cursorCol = left + 2;
1211 pos = 0;
1212 prevWasTab = false;
1213 }
1214 public override void Draw()
1215 {
1216 header.Draw(screen);
1217 DrawFrame();
1218 RtAttrOn(RtColorPair(color));
1219 int n = Top() + Height() - 1;
1220 for (int i = Top() + 1; i < n; ++i;)
1221 {
1222 for (int j = Left() + 1; j < Left() + Width() - Left() - 1; ++j;)
1223 {
1224 screen->MoveCursorTo(i, j);
1225 int index = j - (Left() + 2);
1226 int ch = cast<int>(' ');
1227 if (i == Top() + 1 && index >= 0 && index < line.Length())
1228 {
1229 ch = cast<int>(line[index]);
1230 }
1231 RtAddCh(ch);
1232 }
1233 }
1234 if (!matches.IsEmpty())
1235 {
1236 string m = " " + ToString(matchIndex + 1) + "/" + ToString(matches.Count()) + " ";
1237 screen->MoveCursorTo(n, cast<int>(Width() - 2 - m.Length()));
1238 for (char c : m)
1239 {
1240 RtAddCh(cast<int>(c));
1241 }
1242 }
1243 if (screen->focusedWindow == this)
1244 {
1245 screen->MoveCursorTo(cursorRow, cursorCol);
1246 }
1247 RtAttrOff(RtColorPair(color));
1248 }
1249 public void ClearMatches()
1250 {
1251 prevWasTab = false;
1252 matches.Clear();
1253 matchIndex = 0;
1254 }
1255 public override CommandResult HandleKey(int key)
1256 {
1257 switch (key)
1258 {
1259 case keyEnter:
1260 {
1261 ClearMatches();
1262 Evaluate(debugger, line);
1263 history.Add(line);
1264 historyIndex = cast<int>(history.Count());
1265 line.Clear();
1266 pos = 0;
1267 cursorCol = Left() + 2;
1268 return CommandResult.update;
1269 }
1270 case keyTab:
1271 {
1272 if (!prevWasTab)
1273 {
1274 debugger->FindSymbolsMatchingPrefix(line, matches);
1275 matchIndex = 0;
1276 }
1277 else if (!matches.IsEmpty())
1278 {
1279 matchIndex = cast<int>((matchIndex + 1) % matches.Count());
1280 }
1281 if (matchIndex >= 0 && matchIndex < matches.Count())
1282 {
1283 line = matches[matchIndex];
1284 }
1285 pos = cast<int>(line.Length());
1286 cursorCol = Left() + 2 + pos;
1287 prevWasTab = true;
1288 return CommandResult.update;
1289 }
1290 case keyBackspace:
1291 {
1292 ClearMatches();
1293 if (pos > 0)
1294 {
1295 line = line.Substring(0, pos - 1) + line.Substring(pos);
1296 --pos;
1297 cursorCol = cursorCol - 1;
1298 return CommandResult.update;
1299 }
1300 break;
1301 }
1302 case keyDel:
1303 {
1304 ClearMatches();
1305 if (pos < line.Length())
1306 {
1307 ++pos;
1308 cursorCol = cursorCol + 1;
1309 line = line.Substring(0, pos - 1) + line.Substring(pos);
1310 --pos;
1311 cursorCol = cursorCol - 1;
1312 return CommandResult.update;
1313 }
1314 break;
1315 }
1316 case keyHome:
1317 {
1318 ClearMatches();
1319 pos = 0;
1320 cursorCol = Left() + 2;
1321 return CommandResult.update;
1322 }
1323 case keyEnd:
1324 {
1325 prevWasTab = false;
1326 pos = cast<int>(line.Length());
1327 cursorCol = Left() + 2 + pos;
1328 return CommandResult.update;
1329 }
1330 case keyLeft:
1331 {
1332 ClearMatches();
1333 if (pos > 0)
1334 {
1335 --pos;
1336 cursorCol = cursorCol - 1;
1337 return CommandResult.update;
1338 }
1339 break;
1340 }
1341 case keyRight:
1342 {
1343 ClearMatches();
1344 if (pos < line.Length())
1345 {
1346 ++pos;
1347 cursorCol = cursorCol + 1;
1348 return CommandResult.update;
1349 }
1350 break;
1351 }
1352 case keyUp:
1353 {
1354 ClearMatches();
1355 if (historyIndex > 0)
1356 {
1357 --historyIndex;
1358 if (historyIndex >= 0 && historyIndex < history.Count())
1359 {
1360 line = history[historyIndex];
1361 pos = cast<int>(line.Length());
1362 cursorCol = Left() + 2 + pos;
1363 return CommandResult.update;
1364 }
1365 }
1366 break;
1367 }
1368 case keyDown:
1369 {
1370 ClearMatches();
1371 if (historyIndex < history.Count())
1372 {
1373 ++historyIndex;
1374 if (historyIndex >= 0 && historyIndex < history.Count())
1375 {
1376 line = history[historyIndex];
1377 pos = cast<int>(line.Length());
1378 cursorCol = Left() + 2 + pos;
1379 return CommandResult.update;
1380 }
1381 }
1382 line = ustring();
1383 pos = 0;
1384 cursorCol = Left() + 2;
1385 return CommandResult.update;
1386 }
1387 default:
1388 {
1389 ClearMatches();
1390 uchar c = cast<uchar>(key);
1391 if (IsGraphic(c))
1392 {
1393 if (pos < line.Length())
1394 {
1395 line = line.Substring(0, pos) + ustring(c, 1) + line.Substring(pos);
1396 }
1397 else
1398 {
1399 line.Append(c);
1400 }
1401 cursorCol = cursorCol + 1;
1402 ++pos;
1403 return CommandResult.update;
1404 }
1405 break;
1406 }
1407 }
1408 return CommandResult.cont;
1409 }
1410 private HeaderLine header;
1411 private List<ustring> history;
1412 private ustring line;
1413 private int pos;
1414 private int historyIndex;
1415 private bool prevWasTab;
1416 private List<ustring> matches;
1417 private int matchIndex;
1418 }
1419
1420 public class InputWindow : Window
1421 {
1422 public nothrow InputWindow() : base("Input")
1423 {
1424 header.SetHeader("*ESC* Exit");
1425 }
1426 public void SetScreen(Screen* screen_)
1427 {
1428 screen = screen_;
1429 color = blackOnWhite;
1430 top = screen->rows - screen->commandWindowHeight;
1431 left = 0;
1432 height = screen->commandWindowHeight;
1433 width = screen->cols - left;
1434 cursorRow = top + height - 2;
1435 cursorCol = left + 2;
1436 pos = 0;
1437 }
1438 public override void Draw()
1439 {
1440 header.Draw(screen);
1441 DrawFrame();
1442 RtAttrOn(RtColorPair(color));
1443 int n = Top() + Height() - 1;
1444 for (int i = Top() + 1; i < n; ++i;)
1445 {
1446 for (int j = Left() + 1; j < Left() + Width() - Left() - 1; ++j;)
1447 {
1448 screen->MoveCursorTo(i, j);
1449 int index = j - (Left() + 2);
1450 int ch = cast<int>(' ');
1451 if (i == Top() + 1 && index >= 0 && index < line.Length())
1452 {
1453 ch = cast<int>(line[index]);
1454 }
1455 RtAddCh(ch);
1456 }
1457 }
1458 if (screen->focusedWindow == this)
1459 {
1460 screen->MoveCursorTo(cursorRow, cursorCol);
1461 }
1462 RtAttrOff(RtColorPair(color));
1463 }
1464 public override CommandResult HandleKey(int key)
1465 {
1466 switch (key)
1467 {
1468 case keyEnter:
1469 {
1470 resultLine = line;
1471 line.Clear();
1472 pos = 0;
1473 cursorCol = Left() + 2;
1474 return CommandResult.ret;
1475 }
1476 case keyBackspace:
1477 {
1478 if (pos > 0)
1479 {
1480 line = line.Substring(0, pos - 1) + line.Substring(pos);
1481 --pos;
1482 cursorCol = cursorCol - 1;
1483 return CommandResult.update;
1484 }
1485 break;
1486 }
1487 case keyDel:
1488 {
1489 if (pos < line.Length())
1490 {
1491 ++pos;
1492 cursorCol = cursorCol + 1;
1493 line = line.Substring(0, pos - 1) + line.Substring(pos);
1494 --pos;
1495 cursorCol = cursorCol - 1;
1496 return CommandResult.update;
1497 }
1498 break;
1499 }
1500 case keyHome:
1501 {
1502 pos = 0;
1503 cursorCol = Left() + 2;
1504 return CommandResult.update;
1505 }
1506 case keyEnd:
1507 {
1508 pos = cast<int>(line.Length());
1509 cursorCol = Left() + 2 + pos;
1510 return CommandResult.update;
1511 }
1512 case keyLeft:
1513 {
1514 if (pos > 0)
1515 {
1516 --pos;
1517 cursorCol = cursorCol - 1;
1518 return CommandResult.update;
1519 }
1520 break;
1521 }
1522 case keyRight:
1523 {
1524 if (pos < line.Length())
1525 {
1526 ++pos;
1527 cursorCol = cursorCol + 1;
1528 return CommandResult.update;
1529 }
1530 break;
1531 }
1532 default:
1533 {
1534 uchar c = cast<uchar>(key);
1535 if (IsGraphic(c))
1536 {
1537 if (pos < line.Length())
1538 {
1539 line = line.Substring(0, pos) + ustring(c, 1) + line.Substring(pos);
1540 }
1541 else
1542 {
1543 line.Append(c);
1544 }
1545 cursorCol = cursorCol + 1;
1546 ++pos;
1547 return CommandResult.update;
1548 }
1549 break;
1550 }
1551 }
1552 return CommandResult.cont;
1553 }
1554 private HeaderLine header;
1555 private ustring line;
1556 private int pos;
1557 public ustring resultLine;
1558 }
1559
1560 public class Debugger : cmsx.kernel.Debugger
1561 {
1562 public nothrow Debugger() : screen(), symbolTable(null), breakpoints(), disassembly(&breakpoints), output(), commandWindow(), inputWindow()
1563 {
1564 }
1565 public override void Init(Process* process)
1566 {
1567 screen.Reset(new Screen());
1568 cmsx.kernel.Kernel& kernel = cmsx.kernel.Kernel.Instance();
1569 cmsx.object.ExecutableFile* executable = kernel.Executable();
1570 symbolTable = &executable->GetSymbolTable();
1571 disassembly.SetSymbolTable(symbolTable);
1572 disassembly.SetBreakpoints(&breakpoints);
1573 disassembly.SetScreen(screen.Get());
1574 memory.SetScreen(screen.Get());
1575 output.SetScreen(screen.Get());
1576 commandWindow.SetScreen(screen.Get());
1577 inputWindow.SetScreen(screen.Get());
1578 programEntryPoint = process->entryPoint;
1579 disassembly.SetEntryPointAddress(programEntryPoint);
1580 disassembly.SetInstructionEndAddress(programEntryPoint + executable->GetCodeSection()->dataLength);
1581 uint entryPointInst = ReadInst(process, programEntryPoint);
1582 stepBreakpoints.Add(Breakpoint(Breakpoint.Kind.step, programEntryPoint, entryPointInst));
1583 WriteInst(process, programEntryPoint, debugBreakInst);
1584 disassembly.SetDebugger(this);
1585 disassembly.visible = true;
1586 disassembly.next = &memory;
1587 memory.SetDebugger(this);
1588 memory.visible = false;
1589 memory.next = &output;
1590 output.SetDebugger(this);
1591 output.visible = true;
1592 output.next = &commandWindow;
1593 commandWindow.SetDebugger(this);
1594 commandWindow.visible = true;
1595 commandWindow.next = &disassembly;
1596 inputWindow.visible = false;
1597 inputWindow.next = &inputWindow;
1598 screen->focusedWindow = &disassembly;
1599 state = State.running;
1600 }
1601 public override void Run(Process* process)
1602 {
1603 this->process = process;
1604 machine = &cmsx.machine.GetMachine();
1605 inst = -1;
1606 cmsx.machine.Registers& regs = machine->GetRegisters();
1607 if (state == State.running)
1608 {
1609 pc = regs.GetSpecial(cmsx.machine.Registers.rW);
1610 if (SkipStepBreakpoint(this->pc, regs.Get(cmsx.machine.regFP), *machine))
1611 {
1612 return;
1613 }
1614 inst = RemoveStepBreakpoints(process, pc);
1615 Breakpoint* bp = breakpoints.GetBreakpoint(pc);
1616 if (bp != null)
1617 {
1618 if (inst == -1)
1619 {
1620 inst = bp->inst;
1621 }
1622 }
1623 machine->GetProcessor().ClearBreak();
1624 middle = pc;
1625 screen->Update();
1626 int ch = RtGetCh();
1627 while (ch != keyEscape)
1628 {
1629 ulong prevMiddle = middle;
1630 numInstsInWindow = cast<ulong>(disassembly.Height() - 2);
1631 start = middle - 4u * (numInstsInWindow / 2u);
1632 if (ch == keyResize)
1633 {
1634 screen->GetDimensions();
1635 screen->Update();
1636 }
1637 else if (ch == keyF2)
1638 {
1639 commandWindow.visible = true;
1640 screen->focusedWindow = &commandWindow;
1641 screen->Update();
1642 }
1643 else if (ch == keyF3)
1644 {
1645 disassembly.visible = true;
1646 memory.visible = false;
1647 screen->focusedWindow = &disassembly;
1648 screen->Update();
1649 }
1650 else if (ch == keyF4)
1651 {
1652 output.visible = true;
1653 screen->focusedWindow = &output;
1654 screen->Update();
1655 }
1656 else if (ch == keyF6)
1657 {
1658 disassembly.visible = false;
1659 memory.visible = true;
1660 screen->focusedWindow = &memory;
1661 screen->Update();
1662 }
1663 else if (ch == keyF8)
1664 {
1665 Window* next = screen->focusedWindow->next;
1666 while (!next->visible)
1667 {
1668 next = next->next;
1669 }
1670 screen->focusedWindow = next;
1671 screen->Update();
1672 }
1673 else
1674 {
1675 CommandResult result = screen->focusedWindow->HandleKey(ch);
1676 if (result == CommandResult.ret)
1677 {
1678 return;
1679 }
1680 else if (result == CommandResult.update)
1681 {
1682 screen->Update();
1683 }
1684 }
1685 ch = RtGetCh();
1686 }
1687 }
1688 else if (state == State.exit)
1689 {
1690 int ch = RtGetCh();
1691 while (ch != keyEscape)
1692 {
1693 ch = RtGetCh();
1694 }
1695 }
1696 }
1697 public override void Done()
1698 {
1699 screen.Reset();
1700 }
1701 public override void Exit(Process* process)
1702 {
1703 state = State.exit;
1704 }
1705 public override void WriteOutput(byte* buffer, long count)
1706 {
1707 string s(cast<const char*>(cast<void*>(buffer)), count);
1708 ustring u(ToUtf32(s));
1709 for (uchar c : u)
1710 {
1711 output.AddChar(c, 0u);
1712 }
1713 }
1714 public override bool Eof() const
1715 {
1716 return eof;
1717 }
1718 public override void ResetEof()
1719 {
1720 eof = false;
1721 }
1722 public override string ReadInputLine()
1723 {
1724 if (eof)
1725 {
1726 return string();
1727 }
1728 Window* prevFocusedWindow = screen->focusedWindow;
1729 commandWindow.visible = false;
1730 inputWindow.visible = true;
1731 screen->focusedWindow = &inputWindow;
1732 screen->Update();
1733 int ch = RtGetCh();
1734 string resultLine;
1735 while (true)
1736 {
1737 if (ch == keyEscape)
1738 {
1739 resultLine.Clear();
1740 eof = true;
1741 break;
1742 }
1743 else if (ch == keyControlD)
1744 {
1745 eof = true;
1746 break;
1747 }
1748 else
1749 {
1750 CommandResult result = screen->focusedWindow->HandleKey(ch);
1751 if (result == CommandResult.ret)
1752 {
1753 resultLine = ToUtf8(inputWindow.resultLine) + "\n";
1754 break;
1755 }
1756 else if (result == CommandResult.update)
1757 {
1758 screen->Update();
1759 }
1760 }
1761 ch = RtGetCh();
1762 }
1763 commandWindow.visible = true;
1764 inputWindow.visible = false;
1765 screen->focusedWindow = prevFocusedWindow;
1766 return resultLine;
1767 }
1768 private bool SkipStepBreakpoint(ulong address, ulong fp, cmsx.machine.Machine& machine)
1769 {
1770 for (const Breakpoint& stepBp : stepBreakpoints)
1771 {
1772 if (stepBp.address == address)
1773 {
1774 if (stepBp.fp != 0u)
1775 {
1776 if (stepBp.fp != fp)
1777 {
1778 machine.GetRegisters().SetSpecial(cmsx.machine.Registers.rXX, stepBp.inst);
1779 return true;
1780 }
1781 }
1782 }
1783 }
1784 return false;
1785 }
1786 private long RemoveStepBreakpoints(Process* process, ulong address)
1787 {
1788 long inst = -1;
1789 for (const Breakpoint& stepBp : stepBreakpoints)
1790 {
1791 if (address == stepBp.address)
1792 {
1793 inst = stepBp.inst;
1794 }
1795 WriteInst(process, stepBp.address, stepBp.inst);
1796 }
1797 stepBreakpoints.Clear();
1798 return inst;
1799 }
1800 private uint GetInst(Process* process, ulong pc, long inst)
1801 {
1802 if (inst != -1)
1803 {
1804 return cast<uint>(inst);
1805 }
1806 else
1807 {
1808 Breakpoint* bp = breakpoints.GetBreakpoint(pc);
1809 if (bp != null)
1810 {
1811 return bp->inst;
1812 }
1813 }
1814 return ReadInst(process, pc);
1815 }
1816 public void AddOutputLine(const ustring& line, ulong address)
1817 {
1818 output.AddLine(line, address);
1819 screen->Update();
1820 }
1821 public void ToggleBreakpoint(ulong address)
1822 {
1823 Breakpoint* bp = breakpoints.GetBreakpoint(address);
1824 if (bp != null)
1825 {
1826 WriteInst(process, address, bp->inst);
1827 breakpoints.RemoveBreakpoint(address);
1828 screen->Update();
1829 }
1830 else
1831 {
1832 uint inst = GetInst(process, address, -1);
1833 Breakpoint bp(Breakpoint.Kind.hard, address, inst);
1834 breakpoints.SetBreakpoint(bp);
1835 WriteInst(process, address, debugBreakInst);
1836 screen->Update();
1837 }
1838 }
1839 public void Run()
1840 {
1841 uint i = GetInst(process, pc, inst);
1842 machine->GetRegisters().SetSpecial(cmsx.machine.Registers.rXX, i);
1843 }
1844 public void StepInto()
1845 {
1846 uint i = GetInst(process, pc, inst);
1847 WriteInst(process, pc, i);
1848 Breakpoint* bp = breakpoints.GetBreakpoint(pc);
1849 if (bp != null)
1850 {
1851 machine->GetProcessor().SetBreak(bp->address, debugBreakInst);
1852 }
1853 byte opCode = cast<byte>(i >> 24u);
1854 byte x = cast<byte>(i >> 16u);
1855 byte y = cast<byte>(i >> 8u);
1856 byte z = cast<byte>(i);
1857 cmsx.machine.Instruction* instruction = machine->GetInst(opCode);
1858 ulong prevRV = machine->GetRegisters().GetSpecial(cmsx.machine.Registers.rV);
1859 MemoryTable& memoryTable = process->memoryTable;
1860 machine->GetRegisters().SetSpecial(cmsx.machine.Registers.rV, memoryTable.virtualTranslationRegisterValue);
1861 List<ulong> targetAddresses = instruction->GetTargetAddresses(pc, machine->GetRegisters(), machine->GetMemory(), x, y, z);
1862 machine->GetRegisters().SetSpecial(cmsx.machine.Registers.rV, prevRV);
1863 for (ulong address : targetAddresses)
1864 {
1865 Breakpoint bp(Breakpoint.Kind.step, address, GetInst(process, address, -1));
1866 stepBreakpoints.Add(bp);
1867 WriteInst(process, address, debugBreakInst);
1868 }
1869 machine->GetRegisters().SetSpecial(cmsx.machine.Registers.rXX, i);
1870 if (instruction->IsJumpInst() || i == 0u)
1871 {
1872 machine->GetRegisters().SetPC(pc);
1873 }
1874 }
1875 public void StepOver()
1876 {
1877 uint i = GetInst(process, pc, inst);
1878 byte opCode = cast<byte>(i >> 24u);
1879 if (opCode == cmsx.machine.CALL || opCode == cmsx.machine.CALLI)
1880 {
1881 Breakpoint bp(Breakpoint.Kind.step, pc + 4u, GetInst(process, pc + 4u, -1));
1882 bp.fp = machine->GetRegisters().Get(cmsx.machine.regFP);
1883 stepBreakpoints.Add(bp);
1884 WriteInst(process, pc + 4u, debugBreakInst);
1885 machine->GetRegisters().SetSpecial(cmsx.machine.Registers.rXX, i);
1886 machine->GetRegisters().SetPC(pc);
1887 }
1888 else
1889 {
1890 StepInto();
1891 }
1892 }
1893 public void StepOut()
1894 {
1895 uint i = GetInst(process, pc, inst);
1896 ulong retAddress = 0u;
1897 if (InProlog(i))
1898 {
1899 ulong sp = machine->GetRegisters().Get(cmsx.machine.regSP);
1900 try
1901 {
1902 ReadProcessMemory(*machine, process, sp - 8u, retAddress, 8u, false);
1903 }
1904 catch (const Exception& ex)
1905 {
1906 cmsx.machine.Panic("debugger: could not read process memory: " + ex.ToString());
1907 }
1908 }
1909 else if (InEpilog(i))
1910 {
1911 StepInto();
1912 }
1913 else
1914 {
1915 ulong fp = machine->GetRegisters().Get(cmsx.machine.regFP);
1916 try
1917 {
1918 ReadProcessMemory(*machine, process, fp - 8u, retAddress, 8u, false);
1919 }
1920 catch (const Exception& ex)
1921 {
1922 cmsx.machine.Panic("debugger: could not read process memory: " + ex.ToString());
1923 }
1924 }
1925 Breakpoint bp(Breakpoint.Kind.step, retAddress, GetInst(process, retAddress, -1));
1926 stepBreakpoints.Add(bp);
1927 WriteInst(process, retAddress, debugBreakInst);
1928 }
1929 public ulong GetSymbolValue(const string& id)
1930 {
1931 cmsx.object.Symbol* symbol = symbolTable->GetSymbol(id);
1932 if (symbol != null)
1933 {
1934 if (symbol->value.GetFlag(cmsx.object.Value.Flag.register))
1935 {
1936 return GetRegisterValue(cast<byte>(symbol->value.value));
1937 }
1938 SegmentDescriptor* segmentDescriptor = null;
1939 switch (symbol->segment)
1940 {
1941 case cmsx.object.Segment.text: segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.textSegmentIndex]; break;
1942 case cmsx.object.Segment.data: segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.dataSegmentIndex]; break;
1943 case cmsx.object.Segment.pool: segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.poolSegmentIndex]; break;
1944 case cmsx.object.Segment.stack: segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.stackSegmentIndex]; break;
1945 }
1946 if (segmentDescriptor != null)
1947 {
1948 return segmentDescriptor->baseAddress + segmentDescriptor->startAddress + symbol->value.value;
1949 }
1950 return symbol->value.value;
1951 }
1952 else
1953 {
1954 throw Exception("unknown symbol '" + id + "'");
1955 }
1956 }
1957 public void FindSymbolsMatchingPrefix(const ustring& line, List<ustring>& matches)
1958 {
1959 matches.Clear();
1960 ustring prefix;
1961 long start = 0u;
1962 for (start = line.Length() - 1; start >= 0; --start;)
1963 {
1964 if (line[start] == ' ')
1965 {
1966 break;
1967 }
1968 }
1969 start = start + 1;
1970 prefix = line.Substring(start);
1971 string prefixStr = ToUtf8(prefix);
1972 if (!prefixStr.IsEmpty())
1973 {
1974 for (const UniquePtr<cmsx.object.Symbol>& symbol : symbolTable->Symbols())
1975 {
1976 if (symbol->name.StartsWith(prefixStr))
1977 {
1978 matches.Add(TrimAll(line.Substring(0, start) + u" " + ToUtf32(symbol->name)));
1979 }
1980 }
1981 }
1982 }
1983 public ulong GetRegisterValue(byte regNumber)
1984 {
1985 return machine->GetRegisters().Get(regNumber);
1986 }
1987 public ustring ReadStringFromMemory(ulong address)
1988 {
1989 string s;
1990 byte x = cast<byte>(ReadMemoryContent(address, 1u));
1991 while (x != 0u)
1992 {
1993 s.Append(cast<char>(x));
1994 ++address;
1995 x = cast<byte>(ReadMemoryContent(address, 1u));
1996 }
1997 return ToUtf32(s);
1998 }
1999 public ulong ReadMemoryContent(ulong address, byte size)
2000 {
2001 SegmentDescriptor* segmentDescriptor = null;
2002 if (address >= cmsx.machine.textSegmentBaseAddress && address < cmsx.machine.dataSegmentBaseAddress)
2003 {
2004 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.textSegmentIndex];
2005 }
2006 else if (address >= cmsx.machine.dataSegmentBaseAddress && address < cmsx.machine.poolSegmentBaseAddress)
2007 {
2008 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.dataSegmentIndex];
2009 }
2010 else if (address >= cmsx.machine.poolSegmentBaseAddress && address < cmsx.machine.stackSegmentBaseAddress)
2011 {
2012 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.poolSegmentIndex];
2013 }
2014 else if (address >= cmsx.machine.stackSegmentBaseAddress && address < cmsx.machine.kernelBaseAddress)
2015 {
2016 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.stackSegmentIndex];
2017 }
2018 if (segmentDescriptor == null)
2019 {
2020 throw Exception("invalid access to virtual address #" + ToHexString(address));
2021 }
2022 if (address >= segmentDescriptor->baseAddress + segmentDescriptor->startAddress && address <= segmentDescriptor->baseAddress + segmentDescriptor->startAddress + segmentDescriptor->length)
2023 {
2024 ulong value = 0u;
2025 try
2026 {
2027 cmsx.kernel.ReadProcessMemory(*machine, process, address, value, size, false);
2028 return value;
2029 }
2030 catch (const Exception& ex)
2031 {
2032 throw Exception("could not read process memory from virtual address #" + ToHexString(address) + ": " + ex.Message());
2033 }
2034 }
2035 else
2036 {
2037 throw Exception("invalid access to virtual address #" + ToHexString(address));
2038 }
2039 return 0u;
2040 }
2041 public void ShowAddress(ulong address)
2042 {
2043 SegmentDescriptor* segmentDescriptor = null;
2044 byte segmentIndex = cmsx.machine.numSegments;
2045 if (address >= cmsx.machine.textSegmentBaseAddress && address < cmsx.machine.dataSegmentBaseAddress)
2046 {
2047 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.textSegmentIndex];
2048 segmentIndex = cmsx.machine.textSegmentIndex;
2049 }
2050 else if (address >= cmsx.machine.dataSegmentBaseAddress && address < cmsx.machine.poolSegmentBaseAddress)
2051 {
2052 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.dataSegmentIndex];
2053 segmentIndex = cmsx.machine.dataSegmentIndex;
2054 }
2055 else if (address >= cmsx.machine.poolSegmentBaseAddress && address < cmsx.machine.stackSegmentBaseAddress)
2056 {
2057 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.poolSegmentIndex];
2058 segmentIndex = cmsx.machine.poolSegmentIndex;
2059 }
2060 else if (address >= cmsx.machine.stackSegmentBaseAddress && address < cmsx.machine.kernelBaseAddress)
2061 {
2062 segmentDescriptor = process->memoryTable.segmentDescriptors[cmsx.machine.stackSegmentIndex];
2063 segmentIndex = cmsx.machine.stackSegmentIndex;
2064 }
2065 if (address >= segmentDescriptor->baseAddress + segmentDescriptor->startAddress && address <= segmentDescriptor->baseAddress + segmentDescriptor->startAddress + segmentDescriptor->length)
2066 {
2067 switch (segmentIndex)
2068 {
2069 case cmsx.machine.textSegmentIndex:
2070 {
2071 disassembly.visible = true;
2072 memory.visible = false;
2073 middle = address;
2074 break;
2075 }
2076 case cmsx.machine.dataSegmentIndex: case cmsx.machine.poolSegmentIndex: case cmsx.machine.stackSegmentIndex:
2077 {
2078 memory.visible = true;
2079 disassembly.visible = false;
2080 memory.ShowAddress(segmentIndex, address);
2081 break;
2082 }
2083 }
2084 }
2085 }
2086 public void Trace(ulong count)
2087 {
2088 List<ulong> addresses;
2089 addresses.Add(pc);
2090 ulong sp = machine->GetRegisters().Get(cmsx.machine.regSP);
2091 ulong fp = machine->GetRegisters().Get(cmsx.machine.regFP);
2092 ulong address = pc;
2093 ulong prevFP = pc;
2094 ulong retAddress = 0u;
2095 ulong frame = 0u;
2096 while (frame < count && prevFP != 0u)
2097 {
2098 uint inst = ReadInst(process, address);
2099 if (InProlog(inst))
2100 {
2101 try
2102 {
2103 ReadProcessMemory(*machine, process, sp - 8u, retAddress, 8u, false);
2104 }
2105 catch (const Exception& ex)
2106 {
2107 cmsx.machine.Panic("debugger: could not read process memory: " + ex.ToString());
2108 }
2109 ++frame;
2110 address = retAddress;
2111 if (address != 0u)
2112 {
2113 addresses.Add(address);
2114 }
2115 }
2116 try
2117 {
2118 ReadProcessMemory(*machine, process, fp - 8u, retAddress, 8u, false);
2119 ReadProcessMemory(*machine, process, fp, prevFP, 8u, false);
2120 }
2121 catch (const Exception& ex)
2122 {
2123 cmsx.machine.Panic("debugger: could not read process memory: " + ex.ToString());
2124 }
2125 ++frame;
2126 address = retAddress;
2127 if (address != 0u)
2128 {
2129 addresses.Add(address);
2130 }
2131 fp = prevFP;
2132 }
2133 List<cmsx.object.Symbol*> functionSymbols;
2134 for (const UniquePtr<cmsx.object.Symbol>& symbol : symbolTable->Symbols())
2135 {
2136 if (symbol->value.GetFlag(cmsx.object.Value.Flag.function))
2137 {
2138 functionSymbols.Add(symbol.Get());
2139 }
2140 }
2141 Sort(functionSymbols, ByStart());
2142 for (long i = addresses.Count() - 1; i >= 0; --i;)
2143 {
2144 ulong address = addresses[i];
2145 cmsx.object.Symbol s;
2146 s.start = address;
2147 List<cmsx.object.Symbol*>.ConstIterator it = LowerBound(functionSymbols.CBegin(), functionSymbols.CEnd(), &s, ByStart());
2148 string functionName;
2149 if (it == functionSymbols.CEnd())
2150 {
2151 --it;
2152 }
2153 cmsx.object.Symbol* found = *it;
2154 if (found->start > address && it != functionSymbols.CBegin())
2155 {
2156 --it;
2157 found = *it;
2158 }
2159 if (address >= found->start && address < found->start + found->length)
2160 {
2161 functionName = found->name;
2162 }
2163 output.AddLine(ToUtf32(Format(ToString(i), 3, FormatJustify.right)) + u": #" + ToUtf32(ToHexString(address)) + u" : " + ToUtf32(functionName), address);
2164 }
2165 }
2166 public cmsx.machine.Machine* machine;
2167 public cmsx.kernel.Process* process;
2168 public long inst;
2169 public ulong pc;
2170 public ulong middle;
2171 public ulong start;
2172 public ulong numInstsInWindow;
2173 private State state;
2174 private UniquePtr<Screen> screen;
2175 private cmsx.object.SymbolTable* symbolTable;
2176 private Breakpoints breakpoints;
2177 private List<Breakpoint> stepBreakpoints;
2178 private Disassembly disassembly;
2179 private Memory memory;
2180 private Output output;
2181 private CommandWindow commandWindow;
2182 private InputWindow inputWindow;
2183 private ulong programEntryPoint;
2184 private bool eof;
2185 }
2186 }