|
|
1.1 root 1: /*
2: * Hatari - history.c
3: *
1.1.1.4 ! root 4: * Copyright (C) 2011-2014 by Eero Tamminen
1.1 root 5: *
1.1.1.2 root 6: * This file is distributed under the GNU General Public License, version 2
7: * or at your option any later version. Read the file gpl.txt for details.
1.1 root 8: *
9: * history.c - functions for debugger entry & breakpoint history
10: */
11: const char History_fileid[] = "Hatari history.c : " __DATE__ " " __TIME__;
12:
13: #include <assert.h>
1.1.1.4 ! root 14: #include <errno.h>
1.1 root 15: #include "main.h"
16: #include "debugui.h"
17: #include "debug_priv.h"
18: #include "dsp.h"
19: #include "dsp_core.h"
20: #include "evaluate.h"
1.1.1.4 ! root 21: #include "file.h"
1.1 root 22: #include "history.h"
23: #include "m68000.h"
24: #include "68kDisass.h"
25:
1.1.1.3 root 26: #define HISTORY_ITEMS_MIN 64
1.1.1.2 root 27:
28: history_type_t HistoryTracking;
1.1 root 29:
30: typedef struct {
31: bool shown:1;
32: bool valid:1;
33: bool for_dsp:1;
34: /* reason for debugger entry/breakpoint hit */
35: debug_reason_t reason:8;
36: union {
37: Uint16 dsp;
38: Uint32 cpu;
39: } pc;
40: } hist_item_t;
41:
42: static struct {
1.1.1.3 root 43: unsigned idx; /* index to current history item */
44: unsigned count; /* how many items of history are collected */
45: unsigned limit; /* ring-buffer size */
46: hist_item_t *item; /* ring-buffer */
1.1 root 47: } History;
48:
49:
50: /**
51: * Convert debugger entry/breakpoint entry reason to a string
52: */
1.1.1.2 root 53: static const char* History_ReasonStr(debug_reason_t reason)
1.1 root 54: {
55: switch(reason) {
56: case REASON_CPU_EXCEPTION:
57: return "CPU exception";
58: case REASON_CPU_BREAKPOINT:
59: return "CPU breakpoint";
60: case REASON_DSP_BREAKPOINT:
61: return "DSP breakpoint";
62: case REASON_CPU_STEPS:
63: return "CPU steps";
64: case REASON_DSP_STEPS:
65: return "DSP steps";
66: case REASON_USER:
67: return "User break";
68: default:
69: return "Unknown reason";
70: }
71: }
72:
73:
74: /**
1.1.1.2 root 75: * Set what kind of history is collected.
76: * Clear history if tracking type changes as rest of
77: * data wouldn't then be anymore valid.
1.1 root 78: */
1.1.1.3 root 79: static void History_Enable(history_type_t track, unsigned limit)
1.1 root 80: {
1.1.1.2 root 81: const char *msg;
1.1.1.3 root 82: if (track != HistoryTracking || limit != History.limit) {
83: fprintf(stderr, "Re-allocating & zeroing history due to type/limit change.\n");
84: if (History.item) {
85: free(History.item);
86: }
1.1 root 87: memset(&History, 0, sizeof(History));
1.1.1.3 root 88: History.item = calloc(limit, sizeof(History.item[0]));
89: History.limit = limit;
1.1 root 90: }
1.1.1.2 root 91: switch (track) {
92: case HISTORY_TRACK_NONE:
93: msg = "disabled";
94: break;
95: case HISTORY_TRACK_CPU:
96: msg = "enabled for CPU";
97: break;
98: case HISTORY_TRACK_DSP:
99: msg = "enabled for DSP";
100: break;
101: case HISTORY_TRACK_ALL:
102: msg = "enabled for CPU & DSP";
103: break;
104: default:
105: msg = "error";
106: }
107: HistoryTracking = track;
1.1.1.3 root 108: fprintf(stderr, "History tracking %s (max. %d instructions).\n", msg, limit);
1.1 root 109: }
110:
111: /**
112: * Advance & initialize next history item in ring buffer
113: */
114: static void History_Advance(void)
115: {
116: History.idx++;
1.1.1.3 root 117: History.idx %= History.limit;
1.1 root 118: History.item[History.idx].valid = true;
119: History.item[History.idx].shown = false;
120: History.item[History.idx].reason = REASON_NONE;
121: History.count++;
122: }
123:
124: /**
125: * Add CPU PC to history
126: */
127: void History_AddCpu(void)
128: {
129: Uint32 pc = M68000_GetPC();
130:
131: History_Advance();
132: History.item[History.idx].for_dsp = false;
133: History.item[History.idx].pc.cpu = pc;
134: }
135:
136: /**
137: * Add DSP PC to history
138: */
139: void History_AddDsp(void)
140: {
141: Uint16 pc = DSP_GetPC();
142:
143: History_Advance();
144: History.item[History.idx].for_dsp = true;
145: History.item[History.idx].pc.dsp = pc;
146: }
147:
148: /**
149: * Flag last history entry as debugger entry point, with given reason
150: */
151: void History_Mark(debug_reason_t reason)
152: {
1.1.1.3 root 153: if (History.item) {
154: History.item[History.idx].reason = reason;
155: }
1.1 root 156: }
157:
158: /**
1.1.1.4 ! root 159: * Output collected CPU/DSP debugger/breakpoint history
1.1 root 160: */
1.1.1.4 ! root 161: static Uint32 History_Output(Uint32 count, FILE *fp)
1.1 root 162: {
163: bool show_all;
1.1.1.4 ! root 164: Uint32 retval;
1.1 root 165: int i;
166:
1.1.1.3 root 167: if (History.count > History.limit) {
168: History.count = History.limit;
1.1 root 169: }
170: if (count > History.count) {
171: count = History.count;
172: } else {
173: if (!count) {
174: /* default to all */
175: count = History.count;
176: }
177: }
178: if (count <= 0) {
1.1.1.4 ! root 179: fprintf(stderr, "No history items to show.\n");
! 180: return 0;
1.1 root 181: }
1.1.1.4 ! root 182: retval = count;
1.1 root 183:
184: i = History.idx;
185: show_all = false;
186: if (History.item[i].shown) {
187: /* even last item already shown, show all again */
188: show_all = true;
189: }
1.1.1.3 root 190: i = (i + History.limit - count) % History.limit;
1.1 root 191:
192: while (count-- > 0) {
193: i++;
1.1.1.3 root 194: i %= History.limit;
1.1 root 195: if (!History.item[i].valid) {
1.1.1.4 ! root 196: fprintf(fp, "ERROR: invalid history item %d!", count);
1.1 root 197: }
198: if (History.item[i].shown && !show_all) {
199: continue;
200: }
201: History.item[i].shown = true;
202:
203: if (History.item[i].for_dsp) {
204: Uint16 pc = History.item[i].pc.dsp;
1.1.1.4 ! root 205: DSP_DisasmAddress(fp, pc, pc);
1.1 root 206: } else {
207: Uint32 dummy;
1.1.1.4 ! root 208: Disasm(fp, History.item[i].pc.cpu, &dummy, 1);
1.1 root 209: }
210: if (History.item[i].reason != REASON_NONE) {
1.1.1.4 ! root 211: fprintf(fp, "Debugger: *%s*\n", History_ReasonStr(History.item[i].reason));
1.1 root 212: }
213: }
1.1.1.4 ! root 214: return retval;
! 215: }
! 216:
! 217: /* History_Output() helper for "info" & "lock" commands */
! 218: void History_Show(FILE *fp, Uint32 count)
! 219: {
! 220: History_Output(count, fp);
! 221: }
! 222:
! 223: /*
! 224: * save all history to given file
! 225: */
! 226: static void History_Save(const char *name)
! 227: {
! 228: Uint32 count;
! 229: FILE *fp;
! 230:
! 231: if (File_Exists(name)) {
! 232: fprintf(stderr, "ERROR: file '%s' already exists!\n", name);
! 233:
! 234: } else if ((fp = fopen(name, "w"))) {
! 235: count = History_Output(0, fp);
! 236: fprintf(stderr, "%d history items saved to '%s'.\n", count, name);
! 237: fclose(fp);
! 238: } else {
! 239: fprintf(stderr, "ERROR: opening '%s' failed (%d).\n", name, errno);
! 240: }
1.1 root 241: }
242:
1.1.1.3 root 243: /*
244: * Readline callback
245: */
246: char *History_Match(const char *text, int state)
247: {
1.1.1.4 ! root 248: static const char* cmds[] = { "cpu", "dsp", "off", "save" };
1.1.1.3 root 249: return DebugUI_MatchHelper(cmds, ARRAYSIZE(cmds), text, state);
250: }
251:
1.1 root 252: /**
253: * Command: Show collected CPU/DSP debugger/breakpoint history
254: */
255: int History_Parse(int nArgc, char *psArgs[])
256: {
1.1.1.3 root 257: int count, limit = 0;
1.1 root 258:
1.1.1.3 root 259: if (nArgc < 2) {
260: return DebugUI_PrintCmdHelp(psArgs[0]);
261: }
262: if (nArgc > 2) {
263: limit = atoi(psArgs[2]);
264: }
265: /* make sure value is valid & positive */
266: if (!limit) {
267: limit = History.limit;
268: }
269: if (limit < HISTORY_ITEMS_MIN) {
270: limit = HISTORY_ITEMS_MIN;
1.1 root 271: }
272: count = atoi(psArgs[1]);
1.1.1.3 root 273:
274: if (count <= 0) {
1.1 root 275: /* no count -> enable or disable? */
276: if (strcmp(psArgs[1], "on") == 0) {
1.1.1.3 root 277: History_Enable(HISTORY_TRACK_ALL, limit);
1.1 root 278: return DEBUGGER_CMDDONE;
279: }
280: if (strcmp(psArgs[1], "off") == 0) {
1.1.1.3 root 281: History_Enable(HISTORY_TRACK_NONE, limit);
1.1.1.2 root 282: return DEBUGGER_CMDDONE;
283: }
284: if (strcmp(psArgs[1], "cpu") == 0) {
1.1.1.3 root 285: History_Enable(HISTORY_TRACK_CPU, limit);
1.1.1.2 root 286: return DEBUGGER_CMDDONE;
287: }
288: if (strcmp(psArgs[1], "dsp") == 0) {
1.1.1.3 root 289: History_Enable(HISTORY_TRACK_DSP, limit);
1.1 root 290: return DEBUGGER_CMDDONE;
291: }
1.1.1.4 ! root 292: if (nArgc == 3 && strcmp(psArgs[1], "save") == 0) {
! 293: History_Save(psArgs[2]);
! 294: return DEBUGGER_CMDDONE;
! 295: }
1.1.1.3 root 296: fprintf(stderr, "History range is 1-<limit>\n");
297: return DebugUI_PrintCmdHelp(psArgs[0]);
1.1 root 298: }
299:
1.1.1.4 ! root 300: History_Show(stderr, count);
1.1 root 301: return DEBUGGER_CMDDONE;
302: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.