1 /** Very simple Z80 CPU emulator.
4 #include "cpu/z80_def.h"
15 int disass_z80(FILE *f
, uint32 adr
);
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.
26 /** Exit the emulator (normal exit). */
28 /** Print the character in L to the screen. */
30 /** Returns the run time in 1/100s. */
34 RST_08_PROFILE_ENTER_LEAF
39 CPU_CLOCKS_PER_SECOND
= 100
47 PROFILE_ENTER_OFFSET
= 3
50 #define PROFILE_FILE_NAME "profile.out"
53 /** If set trace execution by dumping the code as it executes. */
55 /** If set the return registers will be dumped when a ret is hit. */
57 /** If set dumps the register contents at each instruction. */
59 /** If set, does an outline depth trace. */
60 int traceOutlineDepth
;
61 /** Loaded map file if any. */
63 /** If set indents the debug output based on the call depth. */
65 /** Current call depth from the start of the program. */
67 /** Total t-states since start. */
68 unsigned long tStates
;
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
, ...)
88 vfprintf(stderr
, format
, ap
);
89 fprintf(stderr
, "\n");
98 if (_G
.profile
.enable
) {
100 FILE *fp
= fopen(PROFILE_FILE_NAME
, "w");
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) {
111 fprintf(fp
, "%s %lu %lu %lu %.2f\n", map_lookup(_G
.pmap
, (uint16
)i
, buffer
),
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
);
122 printf("%lu t-states\n", _G
.tStates
);
128 static void printEnterExitLog(int pc
, int isEnter
)
130 static const char spaces
[] = " ";
131 char buffer
[MAX_LINE
];
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
));
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);
161 if (_G
.traceOutlineDepth
) {
162 int pc
= PC
- PROFILE_ENTER_OFFSET
;
163 printEnterExitLog(pc
, 1);
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
;
181 assert(_G
.callDepth
>= 0);
183 if (_G
.traceOutlineDepth
) {
184 printEnterExitLog(PC
, 0);
188 void returnHook(void)
190 if (_G
.profile
.inLeaf
) {
192 _G
.profile
.inLeaf
= FALSE
;
196 /** System trap implementation. */
197 OPDEF(rst_08
, 0xC7+8)
200 case RST_08_EXIT_EMU
:
206 if (RL
== '\n' || RL
== '\r') {
210 case RST_08_GET_CLOCK
:
211 HL
= _G
.tStates
/ (CPU_SPEED
/CPU_CLOCKS_PER_SECOND
);
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
);
220 case RST_08_PROFILE_EXIT
:
223 case RST_08_PROFILE_ENTER_LEAF
:
224 assert(!_G
.profile
.inLeaf
);
226 _G
.profile
.inLeaf
= TRUE
;
229 printf("Unsupported RST 08 code %02Xh\n", RA
);
234 unsigned char mon_read_byte(unsigned int addr
)
236 return z80_proc
.mem
[addr
];
242 char buffer
[MAX_LINE
];
243 fprintf(stderr
, "%s:\t", map_lookup(_G
.pmap
, PC
, buffer
));
244 disass_z80(stderr
, PC
);
247 fprintf(stderr
, "\tAF: %04X BC: %04X DE: %04X HL: %04X SP: %04X IX: %04X\n", AF
, BC
, DE
, HL
, SP
, IX
);
250 fprintf(stderr
, "\n");
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
);
260 // Dumping is off, do nothing.
264 void profile_z80(int addr
, int ticks
)
269 /** Load the given rom image into memory. */
270 static void loadImage(const char *fname
)
273 FILE *fp
= fopen(fname
, "rb");
276 fatal("Couldn't open the rom image %s for reading.", fp
);
279 got
= fread(z80_proc
.mem
, 1, 65536, fp
);
281 fatal("Error while reading the rom image.");
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
, '=');
305 static int getIntVal(const char *sz
)
307 return atoi(getStrVal(sz
));
310 int main(int argc
, char **argv
)
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")) {
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")) {
336 else if (!strcmp(argv
[i
], "--outlinedepthtrace")) {
337 _G
.traceOutlineDepth
= 1;
339 else if (!strcmp(argv
[i
], "--dumpret")) {
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
]));
364 // Setup the emulation.
367 // Load the given rom image file.
374 // And run for a random amount of time.
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
);