Hackfix and re-enable strtoull and wcstoull, see bug #3798.
[sdcc.git] / sdcc-extra / emu / rrz80 / rrz80.c
blob9d1a70bdb204191f78e62282d6ea8981a04bf052
1 /** Very simple Z80 CPU emulator.
2 */
3 #include "cpu/z80.h"
4 #include "cpu/z80_def.h"
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdarg.h>
9 #include <string.h>
10 #include <time.h>
11 #include <assert.h>
13 #include "types.h"
15 int disass_z80(FILE *f, uint32 adr);
17 enum {
18 FALSE,
19 TRUE
22 /** Names for the system traps. Set A to one of these and call RST 08
23 with parameteres in HL. Any return value will also be in HL.
25 enum {
26 /** Exit the emulator (normal exit). */
27 RST_08_EXIT_EMU,
28 /** Print the character in L to the screen. */
29 RST_08_PUTCHAR,
30 /** Returns the run time in 1/100s. */
31 RST_08_GET_CLOCK,
32 RST_08_PROFILE_ENTER,
33 RST_08_PROFILE_EXIT,
34 RST_08_PROFILE_ENTER_LEAF
37 enum {
38 CPU_SPEED = 4000000,
39 CPU_CLOCKS_PER_SECOND = 100
42 enum {
43 MEMORY_SIZE = 65536
46 enum {
47 PROFILE_ENTER_OFFSET = 3
50 #define PROFILE_FILE_NAME "profile.out"
52 static struct {
53 /** If set trace execution by dumping the code as it executes. */
54 int trace;
55 /** If set the return registers will be dumped when a ret is hit. */
56 int dumpRet;
57 /** If set dumps the register contents at each instruction. */
58 int traceRegs;
59 /** If set, does an outline depth trace. */
60 int traceOutlineDepth;
61 /** Loaded map file if any. */
62 MAP *pmap;
63 /** If set indents the debug output based on the call depth. */
64 int traceWithDepth;
65 /** Current call depth from the start of the program. */
66 int callDepth;
67 /** Total t-states since start. */
68 unsigned long tStates;
69 struct {
70 int enable;
71 unsigned long *ticks;
72 unsigned long *calls;
73 long startTStates;
74 int callStack[1024];
75 int callDepth;
76 int inLeaf;
77 } profile;
78 } _G;
80 /** Display an error message and exit the program. Like throw. Rely
81 on system support for closing any open files, freeing memory etc.
83 static void fatal(const char *format, ...)
85 va_list ap;
86 va_start(ap, format);
88 vfprintf(stderr, format, ap);
89 fprintf(stderr, "\n");
91 va_end(ap);
93 exit(-2);
96 void exitEmu(void)
98 if (_G.profile.enable) {
99 int i;
100 FILE *fp = fopen(PROFILE_FILE_NAME, "w");
102 if (fp == NULL) {
103 fatal("Failure while opening " PROFILE_FILE_NAME);
106 fprintf(fp, "; Function total-ticks total-calls ticks-per-call total-percent\n");
108 for (i = 0; i < MEMORY_SIZE; i++) {
109 if (_G.profile.ticks[i] != 0) {
110 char buffer[128];
111 fprintf(fp, "%s %lu %lu %lu %.2f\n", map_lookup(_G.pmap, (uint16)i, buffer),
112 _G.profile.ticks[i],
113 _G.profile.calls[i],
114 _G.profile.ticks[i]/_G.profile.calls[i],
115 100.0 * _G.profile.ticks[i] / _G.tStates
119 fprintf(fp, "; %lu t-states\n", _G.tStates);
120 fclose(fp);
122 printf("%lu t-states\n", _G.tStates);
125 exit(0);
128 static void printEnterExitLog(int pc, int isEnter)
130 static const char spaces[] = " ";
131 char buffer[MAX_LINE];
133 if (isEnter) {
134 fprintf(stderr, "%s%s %s\n",
135 spaces + sizeof(spaces) - _G.callDepth*2,
136 isEnter ? "Entered " : "Leaving",
137 map_lookup(_G.pmap, pc, buffer));
139 else {
140 fprintf(stderr, "%sLeaving\n",
141 spaces + sizeof(spaces) - _G.callDepth*2
146 static void handleEnter(void)
148 if (_G.profile.enable) {
149 /* Started a new function. */
150 /* Record the time spent in the last function. */
151 _G.profile.ticks[_G.profile.callStack[_G.profile.callDepth]] += _G.tStates - _G.profile.startTStates;
152 /* Re-start the timer */
153 _G.profile.startTStates = _G.tStates;
154 /* Push this fun onto the call stack */
155 _G.profile.callStack[++_G.profile.callDepth] = PC - PROFILE_ENTER_OFFSET;
156 /* Increase the call count. */
157 _G.profile.calls[_G.profile.callStack[_G.profile.callDepth]]++;
158 assert(_G.profile.callDepth < 1023);
159 /* And continue */
161 if (_G.traceOutlineDepth) {
162 int pc = PC - PROFILE_ENTER_OFFSET;
163 printEnterExitLog(pc, 1);
165 _G.callDepth++;
168 static void handleExit(void)
170 if (_G.profile.enable) {
171 /* Record the time spent in this function. */
172 _G.profile.ticks[_G.profile.callStack[_G.profile.callDepth]] += _G.tStates - _G.profile.startTStates;
173 /* Re-start the timer */
174 _G.profile.startTStates = _G.tStates;
175 /* Pop this fun off the call stack */
176 _G.profile.callDepth--;
177 assert(_G.profile.callDepth >= 0);
179 _G.profile.inLeaf = FALSE;
180 _G.callDepth--;
181 assert(_G.callDepth >= 0);
183 if (_G.traceOutlineDepth) {
184 printEnterExitLog(PC, 0);
188 void returnHook(void)
190 if (_G.profile.inLeaf) {
191 handleExit();
192 _G.profile.inLeaf = FALSE;
196 /** System trap implementation. */
197 OPDEF(rst_08, 0xC7+8)
199 switch (RA) {
200 case RST_08_EXIT_EMU:
201 exitEmu();
202 break;
203 case RST_08_PUTCHAR:
204 putchar(RL);
205 fflush(stdout);
206 if (RL == '\n' || RL == '\r') {
207 fflush(stdout);
209 break;
210 case RST_08_GET_CLOCK:
211 HL = _G.tStates / (CPU_SPEED/CPU_CLOCKS_PER_SECOND);
212 break;
213 case RST_08_PROFILE_ENTER:
214 if (_G.profile.inLeaf) {
215 printf("Asserting. Last %04X, this %04X\n", _G.profile.callStack[_G.profile.callDepth], PC);
217 assert(!_G.profile.inLeaf);
218 handleEnter();
219 break;
220 case RST_08_PROFILE_EXIT:
221 handleExit();
222 break;
223 case RST_08_PROFILE_ENTER_LEAF:
224 assert(!_G.profile.inLeaf);
225 handleEnter();
226 _G.profile.inLeaf = TRUE;
227 break;
228 default:
229 printf("Unsupported RST 08 code %02Xh\n", RA);
231 ENDOP();
234 unsigned char mon_read_byte(unsigned int addr)
236 return z80_proc.mem[addr];
239 void debug_z80(void)
241 if (_G.trace) {
242 char buffer[MAX_LINE];
243 fprintf(stderr, "%s:\t", map_lookup(_G.pmap, PC, buffer));
244 disass_z80(stderr, PC);
246 if (_G.traceRegs) {
247 fprintf(stderr, "\tAF: %04X BC: %04X DE: %04X HL: %04X SP: %04X IX: %04X\n", AF, BC, DE, HL, SP, IX);
249 else {
250 fprintf(stderr, "\n");
253 else {
254 // Tracing is off, do nothing.
256 if (_G.dumpRet && mon_read_byte(PC) == 0xC9) {
257 fprintf(stderr, "--- Return: DE:HL = %04X:%04X\n", DE, HL);
259 else {
260 // Dumping is off, do nothing.
264 void profile_z80(int addr, int ticks)
266 _G.tStates += ticks;
269 /** Load the given rom image into memory. */
270 static void loadImage(const char *fname)
272 int got;
273 FILE *fp = fopen(fname, "rb");
275 if (!fp) {
276 fatal("Couldn't open the rom image %s for reading.", fp);
279 got = fread(z80_proc.mem, 1, 65536, fp);
280 if (got < 0) {
281 fatal("Error while reading the rom image.");
284 fclose(fp);
287 static void usage(void)
289 fatal("Usage: rrz80 [--trace] [--outlinedepthtrace] [--tracewithdepth] [--traceregs] [--dumpret] [--maxruntime=xx] [--mapfile=mapfilename] romimage.bin");
292 static int startsWith(const char *sz, const char *szStart)
294 return !strncmp(sz, szStart, strlen(szStart));
297 static char *getStrVal(const char *sz)
299 char *idx = strchr(sz, '=');
300 assert(idx != NULL);
302 return idx+1;
305 static int getIntVal(const char *sz)
307 return atoi(getStrVal(sz));
310 int main(int argc, char **argv)
312 time_t runUntil = 0;
313 int i;
315 const char *fname = NULL;
317 for (i = 1; i < argc; i++) {
318 if (argv[i][0] == '-') {
319 if (startsWith(argv[i], "--maxruntime=")) {
320 runUntil = time(NULL) + getIntVal(argv[i]);
322 else if (!strcmp(argv[i], "--trace")) {
323 _G.trace = 1;
325 else if (!strcmp(argv[i], "--profile")) {
326 _G.profile.enable = 1;
327 _G.profile.ticks = (long unsigned int*) calloc(MEMORY_SIZE, sizeof(*_G.profile.ticks));
328 _G.profile.calls = (long unsigned int*) calloc(MEMORY_SIZE, sizeof(*_G.profile.calls));
329 if (_G.profile.ticks == NULL || _G.profile.calls == NULL) {
330 fatal("Out of memory while allocating profile array");
333 else if (!strcmp(argv[i], "--traceregs")) {
334 _G.traceRegs = 1;
336 else if (!strcmp(argv[i], "--outlinedepthtrace")) {
337 _G.traceOutlineDepth = 1;
339 else if (!strcmp(argv[i], "--dumpret")) {
340 _G.dumpRet = 1;
342 else if (!strcmp(argv[i], "--tracewithdepth")) {
343 _G.traceWithDepth = 1;
345 else if (startsWith(argv[i], "--mapfile=")) {
346 _G.pmap = map_load(getStrVal(argv[i]));
347 if (_G.pmap == NULL) {
348 fatal("Couldn't open the map file %s.", getStrVal(argv[i]));
351 else {
352 usage();
355 else {
356 fname = argv[i];
360 if (fname == NULL) {
361 usage();
364 // Setup the emulation.
365 z80_init();
367 // Load the given rom image file.
368 loadImage(fname);
370 // Reset the emu
371 z80_reset();
373 do {
374 // And run for a random amount of time.
375 z80_step(1000);
376 } while (runUntil == 0 || time(NULL) < runUntil);
378 // Can only get here if the execution doesn't finish in time
379 fprintf(stderr, "%s: timeout in running %s\n", argv[0], fname);
381 return -1;