1 /* src/sh2trace.c: SH-2 tracing code for debugging
2 Copyright 2009 Andrew Church
3
4 This file is part of Yabause.
5
6 Yabause is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Yabause is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Yabause; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*! \file sh2trace.c
22 \brief SH2 execution tracing.
23 */
24
25
26 #include <stdio.h>
27 #include "sh2core.h"
28 #include "sh2d.h"
29 #include "sh2trace.h"
30
31 /*************************************************************************/
32
33 /* Define BINARY_LOG to log traces in a binary format (faster than text) */
34 //#define BINARY_LOG
35
36 /* Define GZIP_LOG to compress log as it's created */
37 #ifdef __linux__
38 # define GZIP_LOG
39 #endif
40
41 /* Define ECHO_TO_STDERR to log traces to stderr as well as logfile
42 * (ignored in BINARY_LOG mode) */
43 // #define ECHO_TO_STDERR
44
45 /*----------------------------------*/
46
47 static const u64 trace_start = 000000000ULL; // First cycle to trace
48 static const u64 trace_stop = 2800000000ULL; // Last cycle to trace + 1
49
50 /*----------------------------------*/
51
52 static int is_ins_enabled = 0;
53
54 static FILE *logfile; // Trace log file
55 static u64 cycle_accum = 0; // Global cycle accumulator
56 static u64 current_cycles = 0; // Cycle count on last call to sh2_trace()
57
58 /*************************************************************************/
59
SH2SetInsTracing(int enable)60 void SH2SetInsTracing(int enable)
61 {
62 is_ins_enabled = enable;
63 }
64
sh2_cycle_count(void)65 FASTCALL u64 sh2_cycle_count(void)
66 {
67 return current_cycles;
68 }
69
70 /*-----------------------------------------------------------------------*/
71
sh2_trace_add_cycles(s32 cycles)72 FASTCALL void sh2_trace_add_cycles(s32 cycles)
73 {
74 cycle_accum += cycles;
75 }
76
77 /*-----------------------------------------------------------------------*/
78
sh2_trace_writeb(u32 address,u32 value)79 FASTCALL void sh2_trace_writeb(u32 address, u32 value)
80 {
81 #ifdef BINARY_LOG
82 struct {
83 u16 id; // 1 = byte store
84 u16 pad1;
85 u32 address;
86 u32 value;
87 u32 pad2;
88 } buf;
89 #endif
90 if (logfile) {
91 value &= 0xFF;
92 #ifdef BINARY_LOG
93 buf.id = 1;
94 buf.pad1 = 0;
95 buf.address = address;
96 buf.value = value;
97 buf.pad2 = 0;
98 fwrite(&buf, sizeof(buf), 1, logfile);
99 #else
100 fprintf(logfile, "WRITEB %08X <- %02X\n", (int)address, (int)value);
101 # ifdef ECHO_TO_STDERR
102 fprintf(stderr, "WRITEB %08X <- %02X\n", (int)address, (int)value);
103 # endif
104 #endif
105 }
106 }
107
sh2_trace_writew(u32 address,u32 value)108 FASTCALL void sh2_trace_writew(u32 address, u32 value)
109 {
110 #ifdef BINARY_LOG
111 struct {
112 u16 id; // 2 = word store
113 u16 pad1;
114 u32 address;
115 u32 value;
116 u32 pad2;
117 } buf;
118 #endif
119 if (logfile) {
120 value &= 0xFFFF;
121 #ifdef BINARY_LOG
122 buf.id = 2;
123 buf.pad1 = 0;
124 buf.address = address;
125 buf.value = value;
126 buf.pad2 = 0;
127 fwrite(&buf, sizeof(buf), 1, logfile);
128 #else
129 fprintf(logfile, "WRITEW %08X <- %04X\n", (int)address, (int)value);
130 # ifdef ECHO_TO_STDERR
131 fprintf(stderr, "WRITEW %08X <- %04X\n", (int)address, (int)value);
132 # endif
133 #endif
134 }
135 }
136
sh2_trace_writel(u32 address,u32 value)137 FASTCALL void sh2_trace_writel(u32 address, u32 value)
138 {
139 if (logfile) {
140 #ifdef BINARY_LOG
141 struct {
142 u16 id; // 4 = long store
143 u16 pad1;
144 u32 address;
145 u32 value;
146 u32 pad2;
147 } buf;
148 buf.id = 4;
149 buf.pad1 = 0;
150 buf.address = address;
151 buf.value = value;
152 buf.pad2 = 0;
153 fwrite(&buf, sizeof(buf), 1, logfile);
154 #else
155 fprintf(logfile, "WRITEL %08X <- %08X\n", (int)address, (int)value);
156 # ifdef ECHO_TO_STDERR
157 fprintf(stderr, "WRITEL %08X <- %08X\n", (int)address, (int)value);
158 # endif
159 #endif
160 }
161 }
162
163 /*-----------------------------------------------------------------------*/
164
165 #ifndef BINARY_LOG
HEXIT(char * const ptr,u32 val,int ndigits)166 static INLINE void HEXIT(char * const ptr, u32 val, int ndigits)
167 {
168 while (ndigits-- > 0) {
169 const int digit = val & 0xF;
170 val >>= 4;
171 ptr[ndigits] = (digit>9 ? digit+7+'0' : digit+'0');
172 }
173 }
174 #endif
175
sh2_trace(SH2_struct * state,u32 address)176 FASTCALL void sh2_trace(SH2_struct *state, u32 address)
177 {
178 if (!is_ins_enabled)
179 return;
180
181 current_cycles = cycle_accum + state->cycles;
182
183 if (current_cycles < trace_start) {
184
185 /* Before first instruction: do nothing */
186
187 } else if (current_cycles >= trace_stop) {
188
189 /* After last instruction: close log file if it's open */
190 if (logfile) {
191 #ifdef GZIP_LOG
192 pclose(logfile);
193 #else
194 fclose(logfile);
195 #endif
196 logfile = NULL;
197 }
198
199 } else {
200 u16 opcode;
201 #ifdef BINARY_LOG
202 struct {
203 u16 id; // 1/2/4 = store; 0x80 = MSH2 insn; 0x81 = SSH2 insn
204 u16 opcode;
205 u32 regs[23];
206 u64 cycles;
207 u32 pad[2];
208 } buf;
209 #else
210 char buf[100];
211 /* This looks ugly, but it's faster than fprintf() in this case */
212 static char regbuf[] = " R0: XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX\n R8: XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX\n PR: XXXXXXXX SR: XXX MAC: XXXXXXXX/XXXXXXXX GBR: XXXXXXXX VBR: XXXXXXXX\n";
213 int i;
214 #endif
215
216 if (!logfile) {
217 const char *filename = "sh2.log";
218 #ifdef GZIP_LOG
219 char cmdbuf[100];
220 snprintf(cmdbuf, sizeof(cmdbuf), "gzip -3 >'%s'.gz", filename);
221 logfile = popen(cmdbuf, "w");
222 #else
223 logfile = fopen(filename, "w");
224 #endif
225 if (!logfile) {
226 return;
227 }
228 setvbuf(logfile, NULL, _IOFBF, 65536);
229 }
230
231 opcode = MappedMemoryReadWordNocache(state, address);
232
233 #ifdef BINARY_LOG
234
235 buf.id = state==SSH2 ? 0x81 : 0x80;
236 buf.opcode = opcode;
237 /* sh2int leaves the branch target in regs.PC during a delay slot,
238 * so insert the proper PC manually */
239 SH2GetRegisters(state, (sh2regs_struct *)buf.regs);
240 buf.regs[22] = address;
241 buf.cycles = current_cycles;
242 buf.pad[0] = 0;
243 buf.pad[1] = 0;
244 fwrite(&buf, sizeof(buf), 1, logfile);
245
246 #else // !BINARY_LOG
247
248 SH2GetRegisters(state, &state->regs);
249
250 SH2Disasm(address, opcode, 0, &state->regs, buf);
251 fprintf(logfile, "[%c] %08X: %04X %-44s [%12llu]\n",
252 state==SSH2 ? 'S' : 'M', (int)address, (int)opcode, buf+12,
253 (unsigned long long)current_cycles);
254 #ifdef ECHO_TO_STDERR
255 fprintf(stderr, "[%c] %08X: %04X %-44s [%12llu]\n",
256 state==SSH2 ? 'S' : 'M', (int)address, (int)opcode, buf+12,
257 (unsigned long long)current_cycles);
258 #endif
259
260 for (i = 0; i < 16; i++) {
261 HEXIT(i>=8 ? ®buf[12+i*9] : ®buf[6+i*9], state->regs.R[i], 8);
262 }
263 HEXIT(®buf[162], state->regs.PR, 8);
264 HEXIT(®buf[176], state->regs.SR.all, 3);
265 HEXIT(®buf[186], state->regs.MACH, 8);
266 HEXIT(®buf[195], state->regs.MACL, 8);
267 HEXIT(®buf[210], state->regs.GBR, 8);
268 HEXIT(®buf[225], state->regs.VBR, 8);
269 fwrite(regbuf, sizeof(regbuf)-1, 1, logfile);
270 #ifdef ECHO_TO_STDERR
271 fwrite(regbuf, sizeof(regbuf)-1, 1, stderr);
272 #endif
273
274 #endif // BINARY_LOG
275
276 } // current_cycles >= trace_start && current_cycles < trace_stop
277 }
278
279 /*************************************************************************/
280
281 /*
282 * Local variables:
283 * c-file-style: "stroustrup"
284 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
285 * indent-tabs-mode: nil
286 * End:
287 *
288 * vim: expandtab shiftwidth=4:
289 */
290