OXYGENHUB
◆ main x16-emulator / src / debugger.c
35.3 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
2
// *******************************************************************************************
3
// *******************************************************************************************
4
//
5
//		File:		debugger.c
6
//		Date:		5th September 2019
7
//		Purpose:	Debugger code
8
//		Author:		Paul Robson ([email protected])
9
//
10
// *******************************************************************************************
11
// *******************************************************************************************
12
13
#include <stdlib.h>
14
#include <stdio.h>
15
#include <string.h>
16
#include <inttypes.h>
17
#include <stdbool.h>
18
#include <SDL.h>
19
#include "glue.h"
20
#include "timing.h"
21
#include "disasm.h"
22
#include "memory.h"
23
#include "video.h"
24
#include "cpu/fake6502.h"
25
#include "debugger.h"
26
#include "rendertext.h"
27
28
static void DEBUGHandleKeyEvent(SDL_Keycode key,int isShift);
29
30
static void DEBUGNumber(int x,int y,int n,int w, SDL_Color colour);
31
static void DEBUGNumberDec(int x, int y, int n, int w, SDL_Color colour);
32
static void DEBUGAddress(int x, int y, int x16Bank, int addr, uint8_t bank, SDL_Color colour);
33
static void DEBUGVAddress(int x, int y, int addr, SDL_Color colour);
34
35
static void DEBUGRenderData(int y,uint32_t data);
36
static int DEBUGRenderZeroPageRegisters(int y);
37
static void DEBUGRenderVERAState(int y);
38
static int DEBUGRenderRegisters(void);
39
static void DEBUGRenderVRAM(int y, int data);
40
static void DEBUGRenderCode(int lines,int initialPC);
41
static void DEBUGRenderStack(int bytesCount);
42
static void DEBUGRenderCmdLine(int x, int width, int height);
43
static bool DEBUGEditCmdLine(SDL_Keycode key);
44
static void DEBUGBuildCmdLine(char *text);
45
static void DEBUGExecCmd();
46
47
// *******************************************************************************************
48
//
49
//		This is the minimum-interference flag. It's designed so that when
50
//		its non-zero DEBUGRenderDisplay() is called.
51
//
52
//			if (showDebugOnRender != 0) {
53
//				DEBUGRenderDisplay(SCREEN_WIDTH,SCREEN_HEIGHT,renderer);
54
//				SDL_RenderPresent(renderer);
55
//				return true;
56
//			}
57
//
58
//		before the SDL_RenderPresent call in video_update() in video.c
59
//
60
//		This controls what is happening. It is at the top of the main loop in main.c
61
//
62
//			if (isDebuggerEnabled != 0) {
63
//				int dbgCmd = DEBUGGetCurrentStatus();
64
//				if (dbgCmd > 0) continue;
65
//				if (dbgCmd < 0) break;
66
//			}
67
//
68
//		Both video.c and main.c require debugger.h to be included.
69
//
70
//		isDebuggerEnabled should be a flag set as a command line switch - without it
71
//		it will run unchanged. It should not be necessary to test the render code
72
//		because showDebugOnRender is statically initialised to zero and will only
73
//		change if DEBUGGetCurrentStatus() is called.
74
//
75
// *******************************************************************************************
76
77
//
78
//				0-9A-F sets the program address, with shift sets the data address.
79
//
80
#define DBGKEY_HOME     SDLK_F1                         // F1 is "Goto PC"
81
#define DBGKEY_RESET    SDLK_F2                         // F2 resets the 6502
82
#define DBGKEY_RUN      SDLK_F5                         // F5 is run.
83
#define DBGKEY_SETBRK   SDLK_F9                         // F9 sets breakpoint
84
#define DBGKEY_STEP     SDLK_F11                        // F11 is step into.
85
#define DBGKEY_STEPOVER SDLK_F10                        // F10 is step over.
86
#define DBGKEY_BANK_NEXT	SDLK_KP_PLUS
87
#define DBGKEY_BANK_PREV	SDLK_KP_MINUS
88
89
#define DBGSCANKEY_BRK  SDL_SCANCODE_F12                // F12 is break into running code.
90
#define DBGSCANKEY_SHOW SDL_SCANCODE_TAB                // Show screen key.
91
                                                        // *** MUST BE SCAN CODES ***
92
93
#define DBGMAX_ZERO_PAGE_REGISTERS 16
94
95
#define DDUMP_RAM	0
96
#define DDUMP_VERA	1
97
98
enum DBG_CMD { CMD_DUMP_MEM='m', CMD_DUMP_VERA='v', CMD_DISASM='d', CMD_SET_BANK='b', CMD_SET_REGISTER='r', CMD_FILL_MEMORY='f' };
99
100
// RGB colours
101
const SDL_Color col_bkgnd= {0, 0, 0, 255};
102
const SDL_Color col_label= {0, 255, 0, 255};
103
const SDL_Color col_data= {0, 255, 255, 255};
104
const SDL_Color col_highlight= {255, 255, 0, 255};
105
const SDL_Color col_cmdLine= {255, 255, 255, 255};
106
107
const SDL_Color col_directpage= {192, 224, 255, 255};
108
109
const SDL_Color col_vram_tilemap = {0, 255, 255, 255};
110
const SDL_Color col_vram_tiledata = {0, 255, 0, 255};
111
const SDL_Color col_vram_special  = {255, 92, 92, 255};
112
const SDL_Color col_vram_other  = {128, 128, 128, 255};
113
114
int showDebugOnRender = 0;                            // Used to trigger rendering in video.c
115
int showFullDisplay = 0;                              // If non-zero show the whole thing.
116
int currentPC = -1;                                   // Current disassembly PC value (16-bit).
117
uint8_t currentPCBank = 0;                            // Current disassembly PC bank (like regs.k) value.
118
int currentData = 0;                                  // Current data display address (RAM or VRAM).
119
int currentPCX16Bank = -1;                            // Current disassembly X16 RAM/ROM bank, -1 unless $A000-$FFFF, .K = 0
120
int currentX16Bank = -1;                              // Current data display (memory dump) X16 RAM/ROM bank
121
int currentMode = DMODE_RUN;                          // Start running.
122
uint32_t debugCPUClocks = 0;
123
124
int dumpmode          = DDUMP_RAM;
125
126
struct breakpoint breakPoint = { -1, 0, -1 };         // User Break
127
struct breakpoint stepBreakPoint = { -1, 0, -1 };     // Single step break.
128
char cmdLine[64]= "";                                 // command line buffer
129
int currentPosInLine= 0;                              // cursor position in the buffer (NOT USED _YET_)
130
int currentLineLen= 0;                                // command line buffer length
131
132
int    oldRegisters[DBGMAX_ZERO_PAGE_REGISTERS];      // Old ZP Register values, for change detection
133
char * oldRegChange[DBGMAX_ZERO_PAGE_REGISTERS];      // Change notification flags for output
134
int    oldRegisterTicks = 0;                          // Last PC when change notification was run
135
136
//
137
//		This flag controls
138
//
139
140
SDL_Renderer *dbgRenderer;                            // Renderer passed in.
141
142
static inline int getCurrentBank(int pc, uint8_t bank) {
143
	int x16Bank = -1;
144
	if (pc >= 0xA000 && bank == 0) {
145
		x16Bank = pc < 0xC000 ? memory_get_ram_bank() : memory_get_rom_bank();
146
	}
147
	return x16Bank;
148
}
149
150
// *******************************************************************************************
151
//
152
//      	This determines if we have hit a breakpoint, both in pc and bank
153
//
154
// *******************************************************************************************
155
156
static inline bool hitBreakpoint(int pc, uint8_t bank, struct breakpoint bp) {
157
	if ((pc == bp.pc) && bank == bp.bank && getCurrentBank(pc, bank) == bp.x16Bank) {
158
		return true;
159
	}
160
	return false;
161
}
162
163
// *******************************************************************************************
164
//
165
//			This is used to determine who is in control. If it returns zero then
166
//			everything runs normally till the next call.
167
//			If it returns +ve, then it will update the video, and loop round.
168
//			If it returns -ve, then exit.
169
//
170
// *******************************************************************************************
171
172
int  DEBUGGetCurrentStatus(void) {
173
174
	SDL_Event event;
175
	if (currentPC < 0) currentPC = regs.pc;                                      // Initialise current PC displayed.
176
177
	if (currentMode == DMODE_STEP) {                                // Single step before
178
		if (currentPC != regs.pc || currentPCBank != regs.k || currentPCX16Bank != getCurrentBank(regs.pc, regs.k)) { // Ensure that the PC moved
179
			currentPC = regs.pc;                                    // Update current PC
180
			currentPCBank = regs.k;
181
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);     // Update the bank if we are in upper memory.
182
			currentMode = DMODE_STOP;                               // So now stop, as we've done it.
183
		}
184
	}
185
186
	if ((currentMode != DMODE_STOP) && (hitBreakpoint(regs.pc, regs.k, breakPoint) || hitBreakpoint(regs.pc, regs.k, stepBreakPoint))) {       // Hit a breakpoint.
187
		currentPC = regs.pc;                                    // Update current PC
188
		currentPCBank = regs.k;
189
		currentPCX16Bank = getCurrentBank(regs.pc, regs.k);     // Update the bank if we are in upper memory.
190
		currentMode = DMODE_STOP;                               // So now stop, as we've done it.
191
		stepBreakPoint.pc = -1;                                 // Clear step breakpoint.
192
		stepBreakPoint.bank = 0;
193
		stepBreakPoint.x16Bank = -1;
194
	}
195
196
	if (SDL_GetKeyboardState(NULL)[DBGSCANKEY_BRK]) {            // Stop on break pressed.
197
		currentMode = DMODE_STOP;
198
		currentPC = regs.pc;                                     // Set the PC to what it is.
199
		currentPCBank = regs.k;
200
		currentPCX16Bank = getCurrentBank(regs.pc, regs.k);      // Update the bank if we are in upper memory.
201
	}
202
203
	if (currentPCX16Bank<0 && currentPC >= 0xA000 && currentPCBank == 0) {
204
		currentPCX16Bank = currentPC < 0xC000 ? memory_get_ram_bank() : memory_get_rom_bank();
205
	}
206
207
	if (currentMode != DMODE_RUN) {                                     // Not running, we own the keyboard.
208
		showFullDisplay =                                               // Check showing screen.
209
					SDL_GetKeyboardState(NULL)[DBGSCANKEY_SHOW];
210
		while (SDL_PollEvent(&event)) {                                 // We now poll events here.
211
			switch(event.type) {
212
				case SDL_QUIT:                                  // Time for exit
213
					return -1;
214
215
				case SDL_KEYDOWN:                               // Handle key presses.
216
					DEBUGHandleKeyEvent(event.key.keysym.sym, event.key.keysym.mod & (KMOD_LSHIFT|KMOD_RSHIFT));
217
					break;
218
219
				case SDL_TEXTINPUT:
220
					DEBUGBuildCmdLine(event.text.text);
221
					break;
222
			}
223
		}
224
	}
225
226
	showDebugOnRender = (currentMode != DMODE_RUN);                         // Do we draw it - only in RUN mode.
227
	if (currentMode == DMODE_STOP) {                                        // We're in charge.
228
		video_update();
229
		SDL_Delay(10);
230
		return 1;
231
	}
232
233
	return 0;                                                               // Run wild, run free.
234
}
235
236
// *******************************************************************************************
237
//
238
//								Setup fonts and co
239
//
240
// *******************************************************************************************
241
void DEBUGInitUI(SDL_Renderer *pRenderer) {
242
		DEBUGInitChars(pRenderer);
243
		dbgRenderer = pRenderer;				// Save renderer.
244
}
245
246
// *******************************************************************************************
247
//
248
//								Setup fonts and co
249
//
250
// *******************************************************************************************
251
void DEBUGFreeUI() {
252
}
253
254
// *******************************************************************************************
255
//
256
//								Set a new breakpoint address. -1 to disable.
257
//
258
// *******************************************************************************************
259
260
void DEBUGSetBreakPoint(struct breakpoint newBreakPoint) {
261
	breakPoint = newBreakPoint;
262
}
263
264
// *******************************************************************************************
265
//
266
//								Break into debugger from code.
267
//
268
// *******************************************************************************************
269
270
void DEBUGBreakToDebugger(void) {
271
	currentMode = DMODE_STOP;
272
	currentPC = regs.pc;
273
	currentPCBank = regs.k;
274
	currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
275
}
276
277
// *******************************************************************************************
278
//
279
//									Handle keyboard state.
280
//
281
// *******************************************************************************************
282
283
static void DEBUGHandleKeyEvent(SDL_Keycode key, int isShift) {
284
	int opcode;
285
286
	switch(key) {
287
288
		case DBGKEY_STEP:      // Single step (F11 by default)
289
			currentMode = DMODE_STEP;  // Runs once, then switches back.
290
			currentPC = regs.pc;
291
			currentPCBank = regs.k;
292
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
293
			debugCPUClocks = clockticks6502;
294
			break;
295
296
		case DBGKEY_STEPOVER:  // Step over (F10 by default)
297
			opcode = debug_read6502(regs.pc, regs.k, currentPCX16Bank); // What opcode is it ?
298
			if (opcode == 0x20 || opcode == 0xFC || opcode == 0x22) {   // Is it JSR or JSL ?
299
				stepBreakPoint.pc = regs.pc + 3 + (opcode == 0x22);  // Then break 3 / 4 on.
300
				stepBreakPoint.bank = regs.k;
301
				stepBreakPoint.x16Bank = getCurrentBank(regs.pc, regs.k);
302
				currentMode = DMODE_RUN;                // And run.
303
				debugCPUClocks = clockticks6502;
304
				timing_init();
305
			} else {
306
				currentMode = DMODE_STEP;               // Otherwise single step.
307
				currentPC = regs.pc;
308
				currentPCBank = regs.k;
309
				currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
310
				debugCPUClocks = clockticks6502;
311
			}
312
			break;
313
314
		case DBGKEY_RUN:                                // F5 Runs until Break.
315
			currentMode = DMODE_RUN;
316
			debugCPUClocks = clockticks6502;
317
			timing_init();
318
			break;
319
320
		case DBGKEY_SETBRK:                             // F9 Set breakpoint to displayed.
321
			if (breakPoint.pc == currentPC && breakPoint.bank == currentPCBank && breakPoint.x16Bank == currentPCX16Bank) {
322
				// Clear (unset) breakpoint by pressing DBGKEY_SETBRK twice
323
				breakPoint.pc = -1;
324
				breakPoint.bank = 0;
325
				breakPoint.x16Bank = -1;
326
			} else {
327
				breakPoint.pc = currentPC;
328
				breakPoint.bank = currentPCBank;
329
				breakPoint.x16Bank = currentPCX16Bank;
330
			}
331
			break;
332
333
		case DBGKEY_HOME:                               // F1 sets the display PC to the actual one.
334
			currentPC = regs.pc;
335
			currentPCBank = regs.k;
336
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
337
			break;
338
339
		case DBGKEY_RESET:                              // F2 reset the 6502
340
			reset6502(regs.is65c816);
341
			currentPC = regs.pc;
342
			currentPCBank = regs.k;
343
			currentPCX16Bank = -1;
344
			break;
345
346
		case DBGKEY_BANK_NEXT:
347
			currentX16Bank += 1;
348
			break;
349
350
		case DBGKEY_BANK_PREV:
351
			currentX16Bank -= 1;
352
			break;
353
354
		case SDLK_PAGEDOWN:
355
			if (isShift) {
356
				currentPC = (currentPC + 0x10) & 0xFFFF;
357
				if (currentPCBank == 0 && currentPC < 0xA000) {
358
					currentPCX16Bank = -1;
359
				}
360
			} else {
361
				if (dumpmode == DDUMP_RAM) {
362
					currentData = (currentData + 0x100) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
363
				} else {
364
					currentData = (currentData + 0x200) & 0x1FFFF;
365
				}
366
			}
367
			break;
368
369
		case SDLK_PAGEUP:
370
			if (isShift) {
371
				currentPC = (currentPC - 0x10) & 0xFFFF;
372
				if (currentPCBank == 0 && currentPC < 0xA000) {
373
					currentPCX16Bank = -1;
374
				}
375
			} else {
376
				if (dumpmode == DDUMP_RAM) {
377
					currentData = (currentData - 0x100) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
378
				} else {
379
					currentData = (currentData - 0x200) & 0x1FFFF;
380
				}
381
			}
382
			break;
383
384
		case SDLK_DOWN:
385
			if (isShift) {
386
				currentPC = (currentPC + 1) & 0xFFFF;
387
				if (currentPCBank == 0 && currentPC < 0xA000) {
388
					currentPCX16Bank = -1;
389
				}
390
			} else {
391
				if (dumpmode == DDUMP_RAM) {
392
					currentData = (currentData + 0x08) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
393
				} else {
394
					currentData = (currentData + 0x10) & 0x1FFFF;
395
				}
396
			}
397
			break;
398
399
		case SDLK_UP:
400
			if (isShift) {
401
				currentPC = (currentPC - 1) & 0xFFFF;
402
				if (currentPCBank == 0 && currentPC < 0xA000) {
403
					currentPCX16Bank = -1;
404
				}
405
			} else {
406
				if (dumpmode == DDUMP_RAM) {
407
					currentData = (currentData - 0x08) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
408
				} else {
409
					currentData = (currentData - 0x10) & 0x1FFFF;
410
				}
411
			}
412
			break;
413
414
		default:
415
			if(DEBUGEditCmdLine(key)) {
416
				// printf("cmd line: %s\n", cmdLine);
417
				DEBUGExecCmd();
418
			}
419
			break;
420
	}
421
422
}
423
424
static void DEBUGBuildCmdLine(char *text) {
425
	uint8_t *ptr = (uint8_t *)text;
426
	while(currentLineLen < sizeof(cmdLine)-1 && *ptr != 0) {
427
		if (*ptr < 0x80) { // We only care about characters in ASCII range.
428
		                   // UTF-8 encodings will always have the high bit
429
		                   // set for any byte which is part of a multibyte
430
		                   // character, so we need not parse it whatsoever.
431
			uint8_t key = *ptr;
432
433
			if (key >= 0x20 && key <= 0x7D) {
434
				if (key >= 'A' && key <= 'Z') {
435
					key += 0x20; // case-fold to lower
436
				}
437
				cmdLine[currentPosInLine++]= key;
438
				if(currentPosInLine > currentLineLen) {
439
					currentLineLen++;
440
					cmdLine[currentLineLen]= 0;
441
				}
442
			}
443
		}
444
		ptr++;
445
	}
446
}
447
448
static bool DEBUGEditCmdLine(SDL_Keycode key) {
449
	// right now, let's have a rudimentary input: only backspace to delete last char
450
	// later, I want a real input line with delete, backspace, left and right cursor
451
	// devs like their comfort ;)
452
	if(key == SDLK_BACKSPACE) {
453
		currentPosInLine--;
454
		if(currentPosInLine<0) {
455
			currentPosInLine= 0;
456
		}
457
		currentLineLen--;
458
		if(currentLineLen<0) {
459
			currentLineLen= 0;
460
		}
461
		cmdLine[currentLineLen]= 0;
462
	}
463
	return (key == SDLK_RETURN) || (key == SDLK_KP_ENTER);
464
}
465
466
static void DEBUGExecCmd() {
467
	int number, bnumber, addr, size, incr;
468
	char reg[10];
469
	char cmd;
470
	char *line= ltrim(cmdLine);
471
472
	cmd= *line;
473
	if(*line) {
474
		line++;
475
	}
476
	// printf("cmd:%c line: '%s'\n", cmd, line);
477
478
	switch (cmd) {
479
		case CMD_DUMP_MEM:
480
			if (sscanf(line, "%x:%x", &bnumber, &number) == 2) {
481
				currentX16Bank = bnumber & 0xFF;
482
			} else if (sscanf(line, "%x", &number) == 1) {
483
				if (!is_gen2) {
484
					currentX16Bank = (number >> 16) & 0xFF;
485
				}
486
			} else {
487
				break;
488
			}
489
			addr = number & (is_gen2 ? 0xFFFFFF : 0xFFFF);
490
			currentData = addr;
491
			dumpmode    = DDUMP_RAM;
492
			break;
493
494
		case CMD_DUMP_VERA:
495
			if (sscanf(line, "%x", &number) == 1) {
496
				addr = number & 0x1FFFF;
497
				currentData = addr;
498
				dumpmode    = DDUMP_VERA;
499
			}
500
			break;
501
502
		case CMD_FILL_MEMORY:
503
			size = 1;
504
			incr = 1;
505
			if (sscanf(line, "%x %x %d %d", &addr, &number, &size, &incr) >= 2) {
506
				if (dumpmode == DDUMP_RAM) {
507
					addr &= 0xFFFFFF;
508
					do {
509
						if (addr >= 0xC000 && addr < 0x10000) {
510
							// Nop.
511
						} else if (addr >= 0xA000 && addr < 0xC000) {
512
							BRAM[(currentX16Bank << 13) + addr - 0xA000] = number;
513
						} else if ((addr >> 16) < num_banks) {
514
							RAM[addr] = number;
515
						}
516
						if (incr) {
517
							addr += incr;
518
						} else {
519
							++addr;
520
						}
521
						addr &= 0xFFFFFF;
522
						--size;
523
					} while (size > 0);
524
				} else {
525
					addr &= 0x1FFFF;
526
					do {
527
						video_space_write(addr, number);
528
						if (incr) {
529
							addr += incr;
530
						} else {
531
							++addr;
532
						}
533
						addr &= 0x1FFFF;
534
						--size;
535
					} while (size > 0);
536
				}
537
			}
538
			break;
539
540
		case CMD_DISASM:
541
			if (sscanf(line, "%x:%x", &bnumber, &number) == 2) {
542
				currentPCX16Bank = (number >= 0xA000 && number <= 0xFFFF) ? bnumber & 0xFF : -1;
543
			} else if (sscanf(line, "%x", &number) == 1) {
544
				if (!is_gen2) {
545
					currentPCX16Bank = (number & 0xFFFF) >= 0xA000 ? (number >> 16) & 0xFF : -1;
546
				} else if (number < 0x00A000 || number >= 0x010000) { // treat input as 24-bit (K+PC)
547
					currentPCX16Bank = -1; // if outside of the X16 banked range, set to undef (-1)
548
				}
549
			} else {
550
				break;
551
			}
552
553
			addr = number & (is_gen2 ? 0xFFFFFF : 0xFFFF);
554
555
			currentPC = addr & 0xFFFF;
556
			if (is_gen2) {
557
				currentPCBank = (addr >> 16) & 0xFF;
558
			}
559
			break;
560
561
		case CMD_SET_BANK:
562
			if (sscanf(line, "%s %d", reg, &number) != 2) {
563
				break;
564
			}
565
566
			if (!strcmp(reg, "rom")) {
567
				memory_set_rom_bank(number & 0x00FF);
568
			} else if (!strcmp(reg, "ram")) {
569
				memory_set_ram_bank(number & 0x00FF);
570
			}
571
			break;
572
573
		case CMD_SET_REGISTER:
574
			if (sscanf(line, "%s %x", reg, &number) != 2) {
575
				break;
576
			}
577
578
			if(!strcmp(reg, "pc")) {
579
				regs.pc= number & 0xFFFF;
580
				waiting = 0;
581
			}
582
			if(!strcmp(reg, "a")) {
583
				regs.a= number & 0x00FF;
584
			}
585
			if(!strcmp(reg, "b")) {
586
				regs.b= number & 0x00FF;
587
			}
588
			if(!strcmp(reg, "c")) {
589
				regs.c= number & 0xFFFF;
590
			}
591
			if(!strcmp(reg, "d")) {
592
				regs.dp= number & 0xFFFF;
593
			}
594
			if(!strcmp(reg, "k")) {
595
				regs.k= number & 0x00FF;
596
			}
597
			if(!strcmp(reg, "dbr")) {
598
				regs.db= number & 0x00FF;
599
			}
600
			if(!strcmp(reg, "x")) {
601
				if (regs.e) {
602
					regs.xl= number & 0x00FF;
603
				} else {
604
					regs.x= number & 0xFFFF;
605
				}
606
			}
607
			if(!strcmp(reg, "y")) {
608
				if (regs.e) {
609
					regs.yl= number & 0x00FF;
610
				} else {
611
					regs.y= number & 0xFFFF;
612
				}
613
			}
614
			if(!strcmp(reg, "sp")) {
615
				if (regs.e) {
616
					regs.sp= 0x100 | (number & 0x00FF);
617
				} else {
618
					regs.sp= number & 0xFFFF;
619
				}
620
			}
621
			break;
622
623
		default:
624
			break;
625
	}
626
627
	currentPosInLine= currentLineLen= *cmdLine= 0;
628
}
629
630
// *******************************************************************************************
631
//
632
//							Render the emulator debugger display.
633
//
634
// *******************************************************************************************
635
636
void DEBUGRenderDisplay(int width, int height) {
637
	if (showFullDisplay) return;								// Not rendering debug.
638
639
	SDL_Rect rc;
640
	rc.w = DBG_WIDTH * 6 * CHAR_SCALE;							// Erase background, set up rect
641
	rc.h = height;
642
	xPos = width-rc.w;yPos = 0; 								// Position screen
643
	rc.x = xPos;rc.y = yPos; 									// Set rectangle and black out.
644
	SDL_SetRenderDrawColor(dbgRenderer,0,0,0,SDL_ALPHA_OPAQUE);
645
	SDL_RenderFillRect(dbgRenderer,&rc);
646
647
	int register_lines = DEBUGRenderRegisters();							// Draw register name and values.
648
	DEBUGRenderCode(register_lines, currentPC);							// Render 6502 disassembly.
649
	if (dumpmode == DDUMP_RAM) {
650
		DEBUGRenderData(register_lines + 1, currentData);
651
		int zp_lines = DEBUGRenderZeroPageRegisters(register_lines + 1);
652
		DEBUGRenderVERAState(zp_lines + 1);
653
	} else {
654
		DEBUGRenderVRAM(register_lines + 1, currentData);
655
	}
656
	DEBUGRenderStack(register_lines);
657
658
	DEBUGRenderCmdLine(xPos, rc.w, height);
659
}
660
661
// *******************************************************************************************
662
//
663
//									 Render command Line
664
//
665
// *******************************************************************************************
666
667
static void DEBUGRenderCmdLine(int x, int width, int height) {
668
	char buffer[sizeof(cmdLine)+1];
669
670
	SDL_SetRenderDrawColor(dbgRenderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
671
	SDL_RenderDrawLine(dbgRenderer, x, height-12, x+width, height-12);
672
673
	sprintf(buffer, ">%s", cmdLine);
674
	DEBUGString(dbgRenderer, 0, DBG_HEIGHT-1, buffer, col_cmdLine);
675
	DEBUGString(dbgRenderer, currentPosInLine+1, DBG_HEIGHT-1, "_", col_cmdLine); // crude cursor
676
}
677
678
// *******************************************************************************************
679
//
680
//									 Render Zero Page Registers
681
//
682
// *******************************************************************************************
683
684
static int DEBUGRenderZeroPageRegisters(int y) {
685
#define LAST_R 15
686
	unsigned char reg = 0;
687
	int y_start = y;
688
	char lbl[6];
689
	while (reg < DBGMAX_ZERO_PAGE_REGISTERS) {
690
		if (((y-y_start) % 5) != 0) {           // Break registers into groups of 5, easier to locate
691
			if (reg <= LAST_R)
692
				sprintf(lbl, "R%d", reg);
693
			else
694
				sprintf(lbl, "x%d", reg);
695
696
			DEBUGString(dbgRenderer, DBG_ZP_REG, y, lbl, col_label);
697
698
			int reg_addr = 2 + reg * 2;
699
			int n = debug_read6502(direct_page_add(reg_addr+1), 0, USE_CURRENT_X16_BANK)*256+debug_read6502(direct_page_add(reg_addr), 0, USE_CURRENT_X16_BANK);
700
701
			DEBUGNumber(DBG_ZP_REG+5, y, n, 4, col_data);
702
703
			if (oldRegChange[reg] != NULL)
704
				DEBUGString(dbgRenderer, DBG_ZP_REG+9, y, oldRegChange[reg], col_data);
705
706
			if (oldRegisterTicks != clockticks6502) {   // change detection only when the emulated CPU changes
707
				oldRegChange[reg] = n != oldRegisters[reg] ? "*" : " ";
708
				oldRegisters[reg]=n;
709
			}
710
			reg++;
711
		}
712
		y++;
713
	}
714
715
	if (oldRegisterTicks != clockticks6502) {
716
		oldRegisterTicks = clockticks6502;
717
	}
718
719
	return y;
720
}
721
722
// *******************************************************************************************
723
//
724
//									 Render Data Area
725
//
726
// *******************************************************************************************
727
728
static void DEBUGRenderData(int y,uint32_t data) {
729
	while (y < DBG_HEIGHT-2) {									// To bottom of screen
730
		DEBUGAddress(DBG_MEMX, y, (uint8_t)currentX16Bank, data & 0xFFFF, data >> 16, col_label);	// Show label.
731
732
		for (int i = 0;i < 8;i++) {
733
			bool isDP = (data >> 16) == 0 && ((data+i - regs.dp) & 0xffff) < 256;
734
			int byte = debug_read6502((data+i) & 0xFFFF, data >> 16, currentX16Bank);
735
			DEBUGNumber(DBG_MEMX+8+i*3,y,byte,2, isDP ? col_directpage : col_data);
736
			DEBUGWrite(dbgRenderer, DBG_MEMX+33+i,y,byte, isDP ? col_directpage : col_data);
737
		}
738
		y++;
739
		data = (data + 8) & 0xFFFFFF;
740
	}
741
}
742
743
static void DEBUGRenderVRAM(int y, int data) {
744
	while (y < DBG_HEIGHT - 2) {                                                   // To bottom of screen
745
		DEBUGVAddress(DBG_MEMX, y, data & 0x1FFFF, col_label); // Show label.
746
747
		for (int i = 0; i < 16; i++) {
748
			int addr = (data + i) & 0x1FFFF;
749
			int byte = video_space_read(addr);
750
751
			if (video_is_tilemap_address(addr)) {
752
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_tilemap);
753
			} else if (video_is_tiledata_address(addr)) {
754
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_tiledata);
755
			} else if (video_is_special_address(addr)) {
756
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_special);
757
			} else {
758
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_other);
759
			}
760
		}
761
		y++;
762
		data += 16;
763
	}
764
}
765
766
// *******************************************************************************************
767
//
768
//									 Render Disassembly
769
//
770
// *******************************************************************************************
771
772
static void DEBUGRenderCode(int lines, int initialPC) {
773
	char buffer[32];
774
	uint8_t implied_status = regs.status;
775
	uint8_t implied_e = regs.e;
776
	uint8_t opcode, operand, carry;
777
	int implied_x16_bank = currentPCX16Bank;
778
779
	for (int y = 0; y < lines; y++) { 							// Each line
780
		DEBUGAddress(DBG_ASMX, y, implied_x16_bank, initialPC, currentPCBank, col_label);
781
		int32_t eff_addr;
782
783
		// Attempt to display the disassembly correctly more often
784
		// if the code logic is reasonably straightforward with respect
785
		// to status flags changing in the immediate instruction list
786
787
		// This doesn't predict status flags changes except by the few
788
		// opcodes below. The PLP instruction, for instance, could easily
789
		// render disassembly following it invalid, but this would have
790
		// still been true without the added logic, anyway.
791
792
		if (regs.is65c816) {
793
			opcode = debug_read6502(initialPC, currentPCBank, currentPCX16Bank);
794
			switch (opcode) {
795
				case 0x81: // CLC
796
					implied_status &= ~FLAG_CARRY;
797
					;;
798
				case 0x83: // SEC
799
					implied_status |= FLAG_CARRY;
800
					;;
801
				case 0xC2: // REP
802
					operand = debug_read6502((initialPC+1) & 0xffff, currentPCBank, currentPCX16Bank);
803
					implied_status = ~operand & implied_status;
804
					;;
805
				case 0xE2: // SEP
806
					operand = debug_read6502((initialPC+1) & 0xffff, currentPCBank, currentPCX16Bank);
807
					implied_status = operand | implied_status;
808
					;;
809
				case 0xFB: // XCE
810
					carry = implied_status & FLAG_CARRY;
811
					implied_status = (implied_status & ~FLAG_CARRY) | (implied_e ? FLAG_CARRY : 0);
812
					implied_e = carry != 0;
813
					;;
814
				default:
815
					;;
816
			}
817
			if (implied_e) implied_status |= FLAG_INDEX_WIDTH | FLAG_MEMORY_WIDTH;
818
819
		}
820
		int size = disasm(initialPC, currentPCBank, RAM, buffer, sizeof(buffer), implied_x16_bank, implied_status, &eff_addr);	// Disassemble code
821
		// Output assembly highlighting PC
822
		DEBUGString(dbgRenderer, DBG_ASMX+8, y, buffer, initialPC == regs.pc ? col_highlight : col_data);
823
		// Populate effective address
824
		if (initialPC == regs.pc) {
825
			if (eff_addr < 0) {
826
				DEBUGString(dbgRenderer, DBG_DATX, lines-1, "----", col_data);
827
			} else {
828
				DEBUGNumber(DBG_DATX, lines-1, eff_addr, 4, col_data);
829
			}
830
		}
831
		initialPC = (initialPC + size) & 0xFFFF;										// Forward to next
832
		if (currentPCBank == 0 && initialPC >= 0xA000 && implied_x16_bank == -1) {
833
			implied_x16_bank = memory_get_ram_bank();
834
		}
835
	}
836
}
837
838
// *******************************************************************************************
839
//
840
//									Render Register Display
841
//
842
// *******************************************************************************************
843
844
static char *labels_c816[] = { "NVMXDIZCE","","","A","B","C","X","Y","K","DB","","PC","DP","SP","BKA","BKO","","BRK","EFF", NULL };
845
static char *labels_c02[] = { "NV-BDIZC","","","A","X","Y","","PC","SP","BKA","BKO","","BRK","EFF", NULL };
846
847
static void DEBUGNumberHighByteCondition(int x, int y, int n, bool condition, SDL_Color ifTrue, SDL_Color ifFalse) {
848
	if (condition) {
849
		DEBUGNumber(x, y, n >> 8, 2, ifTrue);
850
		DEBUGNumber(x + 2, y, n & 0xFF, 2, ifFalse);
851
	} else {
852
		DEBUGNumber(x, y, n, 4, ifFalse);
853
	}
854
}
855
856
static int DEBUGRenderRegisters(void) {
857
	int n = 0,yc = 0;
858
	if (regs.is65c816) {
859
		while (labels_c816[n] != NULL) {								// Labels
860
			DEBUGString(dbgRenderer, DBG_LBLX,n,labels_c816[n], col_label);n++;
861
		}
862
		yc++;
863
		DEBUGNumber(DBG_LBLX, yc, (regs.status >> 7) & 1, 1, col_data);
864
		DEBUGNumber(DBG_LBLX+1, yc, (regs.status >> 6) & 1, 1, col_data);
865
		DEBUGNumber(DBG_LBLX+2, yc, (regs.status >> 5) & 1, 1, regs.e ? col_vram_other : col_data);
866
		DEBUGNumber(DBG_LBLX+3, yc, (regs.status >> 4) & 1, 1, regs.e ? col_vram_other : col_data);
867
		DEBUGNumber(DBG_LBLX+4, yc, (regs.status >> 3) & 1, 1, col_data);
868
		DEBUGNumber(DBG_LBLX+5, yc, (regs.status >> 2) & 1, 1, col_data);
869
		DEBUGNumber(DBG_LBLX+6, yc, (regs.status >> 1) & 1, 1, col_data);
870
		DEBUGNumber(DBG_LBLX+7, yc, (regs.status >> 0) & 1, 1, col_data);
871
		DEBUGNumber(DBG_LBLX+8, yc, regs.e, 1, col_data);
872
		yc+= 2;
873
874
		DEBUGNumber(DBG_DATX, yc++, regs.a, 2, col_data);
875
		DEBUGNumber(DBG_DATX, yc++, regs.b, 2, col_data);
876
		DEBUGNumber(DBG_DATX, yc++, regs.c, 4, col_data);
877
		DEBUGNumberHighByteCondition(DBG_DATX, yc++, regs.x, (regs.status >> 4) & 1, col_vram_other, col_data);
878
		DEBUGNumberHighByteCondition(DBG_DATX, yc++, regs.y, (regs.status >> 4) & 1, col_vram_other, col_data);
879
		DEBUGNumber(DBG_DATX, yc++, regs.k, 2, col_data);
880
		DEBUGNumber(DBG_DATX, yc++, regs.db, 2, col_data);
881
		yc++;
882
883
		DEBUGNumber(DBG_DATX, yc++, regs.pc, 4, col_data);
884
		DEBUGNumber(DBG_DATX, yc++, regs.dp, 4, col_data);
885
		DEBUGNumberHighByteCondition(DBG_DATX, yc++, regs.sp, regs.e, col_vram_other, col_data);
886
		DEBUGNumber(DBG_DATX, yc++, memory_get_ram_bank(), 2, col_data);
887
		DEBUGNumber(DBG_DATX, yc++, memory_get_rom_bank(), 2, col_data);
888
		yc++;
889
	} else {
890
		while (labels_c02[n] != NULL) {									// Labels
891
			DEBUGString(dbgRenderer, DBG_LBLX,n,labels_c02[n], col_label);n++;
892
		}
893
		yc++;
894
		DEBUGNumber(DBG_LBLX, yc, (regs.status >> 7) & 1, 1, col_data);
895
		DEBUGNumber(DBG_LBLX+1, yc, (regs.status >> 6) & 1, 1, col_data);
896
		DEBUGNumber(DBG_LBLX+3, yc, (regs.status >> 4) & 1, 1, col_data);
897
		DEBUGNumber(DBG_LBLX+4, yc, (regs.status >> 3) & 1, 1, col_data);
898
		DEBUGNumber(DBG_LBLX+5, yc, (regs.status >> 2) & 1, 1, col_data);
899
		DEBUGNumber(DBG_LBLX+6, yc, (regs.status >> 1) & 1, 1, col_data);
900
		DEBUGNumber(DBG_LBLX+7, yc, (regs.status >> 0) & 1, 1, col_data);
901
		yc+= 2;
902
903
		DEBUGNumber(DBG_DATX, yc++, regs.a, 2, col_data);
904
		DEBUGNumber(DBG_DATX, yc++, regs.xl, 2, col_data);
905
		DEBUGNumber(DBG_DATX, yc++, regs.yl, 2, col_data);
906
		yc++;
907
908
		DEBUGNumber(DBG_DATX, yc++, regs.pc, 4, col_data);
909
		DEBUGNumber(DBG_DATX, yc++, regs.sp|0x100, 4, col_data);
910
		DEBUGNumber(DBG_DATX, yc++, memory_get_ram_bank(), 2, col_data);
911
		DEBUGNumber(DBG_DATX, yc++, memory_get_rom_bank(), 2, col_data);
912
		yc++;
913
914
	}
915
916
	if (breakPoint.pc < 0) {
917
		DEBUGString(dbgRenderer, DBG_DATX, yc++, "--:----", col_data);
918
	} else if (breakPoint.x16Bank < 0) {
919
		if (is_gen2) {
920
			DEBUGNumber(DBG_DATX, yc, breakPoint.bank, 2, col_data);
921
			DEBUGNumber(DBG_DATX+3, yc++, breakPoint.pc, 4, col_data);
922
		} else {
923
			DEBUGString(dbgRenderer, DBG_DATX, yc, "--:", col_data);
924
			DEBUGNumber(DBG_DATX+3, yc++, (uint16_t)breakPoint.pc, 4, col_data);
925
		}
926
	} else {
927
		DEBUGNumber(DBG_DATX, yc, breakPoint.x16Bank, 2, col_data);
928
		DEBUGString(dbgRenderer, DBG_DATX+2, yc, ":", col_data);
929
		DEBUGNumber(DBG_DATX+3, yc++, breakPoint.pc, 4, col_data);
930
	}
931
	yc++;
932
933
	return n; 													// Number of code display lines
934
}
935
936
937
static char *vera_labels[] = { "ADDR0", "ADDR1", "DATA0","DATA1", "CTRL", "VIDEO", "HSCLE", "VSCLE", "FXCTL", "FXMUL", "CACHE", "ACCUM", "", "CLOCKS ELAPSED", NULL };
938
939
static void DEBUGRenderVERAState(int y) {
940
	int n=0;
941
	int yc=y;
942
	while (vera_labels[n] != NULL) { // Labels
943
		DEBUGString(dbgRenderer, DBG_VERA_REGX, yc, vera_labels[n], col_label);n++;yc++;
944
	}
945
946
	yc=y;
947
948
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_address(0), 5, col_data);
949
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_address(1), 5, col_data);
950
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_read(3, true), 2, col_data);
951
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_read(4, true), 2, col_data);
952
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_read(5, true), 2, col_data);
953
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(0), 2, col_data);
954
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(1), 2, col_data);
955
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(2), 2, col_data);
956
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(8), 2, col_data);
957
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(11), 2, col_data);
958
	DEBUGNumber(DBG_VERA_REGX+6, yc, video_get_dc_value(24), 2, col_data);
959
	DEBUGNumber(DBG_VERA_REGX+8, yc, video_get_dc_value(25), 2, col_data);
960
	DEBUGNumber(DBG_VERA_REGX+10, yc, video_get_dc_value(26), 2, col_data);
961
	DEBUGNumber(DBG_VERA_REGX+12, yc++, video_get_dc_value(27), 2, col_data);
962
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_fx_accum(), 8, col_data);
963
964
	yc+=2;
965
	DEBUGNumberDec(DBG_VERA_REGX, yc++, clockticks6502 - debugCPUClocks, 14, col_data);
966
}
967
968
// *******************************************************************************************
969
//
970
//									Render Top of Stack
971
//
972
// *******************************************************************************************
973
974
static void DEBUGRenderStack(int bytesCount) {
975
	uint16_t sp = regs.sp;
976
	increment_wrap_at_page_boundary(&sp);
977
978
	int y= 0;
979
	while (y < bytesCount) {
980
		DEBUGNumber(DBG_STCK,y, sp,4, col_label);
981
		int byte = debug_read6502(sp, 0, USE_CURRENT_X16_BANK);
982
		DEBUGNumber(DBG_STCK+5,y,byte,2, col_data);
983
		DEBUGWrite(dbgRenderer, DBG_STCK+9,y,byte, col_data);
984
		y++;
985
		increment_wrap_at_page_boundary(&sp);
986
	}
987
}
988
989
// *******************************************************************************************
990
//
991
//									Write Hex Constant
992
//
993
// *******************************************************************************************
994
995
static void DEBUGNumber(int x, int y, int n, int w, SDL_Color colour) {
996
	char fmtString[8],buffer[16];
997
	snprintf(fmtString, sizeof(fmtString), "%%0%dX", w);
998
	snprintf(buffer, sizeof(buffer), fmtString, n);
999
	DEBUGString(dbgRenderer, x, y, buffer, colour);
1000
}
1001
1002
// *******************************************************************************************
1003
//
1004
//					Write Decimal Constant with thousands separator
1005
//
1006
// *******************************************************************************************
1007
1008
static void DEBUGNumberDec(int x, int y, int n, int w, SDL_Color colour) {
1009
	char buf1[32], buf2[32];
1010
	int i,j;
1011
	snprintf(buf1, sizeof(buf1), "%d", n);
1012
	buf2[sizeof(buf2)-1] = 0; // null terminate string
1013
	int count = 0;
1014
	for (i=strlen(buf1) - 1, j=sizeof(buf2) - 1; i >= 0 && j > 1; i--) {
1015
		buf2[--j] = buf1[i];
1016
		count++;
1017
1018
		if (count == 3) {
1019
			buf2[--j] = ' ';
1020
			count = 0;
1021
		}
1022
	}
1023
1024
	if (buf2[j] == ' ') j++;
1025
	DEBUGString(dbgRenderer, x+(w-strlen(buf2+j)), y, buf2+j, colour);
1026
}
1027
1028
// *******************************************************************************************
1029
//
1030
//									Write Bank:Address
1031
//
1032
// *******************************************************************************************
1033
static void DEBUGAddress(int x, int y, int x16Bank, int addr, uint8_t bank, SDL_Color colour) {
1034
	char buffer[4];
1035
1036
	if(addr >= 0xA000 && bank == 0) {
1037
		snprintf(buffer, sizeof(buffer), "%.2X:", x16Bank);
1038
	} else if (is_gen2) {
1039
		snprintf(buffer, sizeof(buffer), "%.2X ", bank);
1040
	} else {
1041
		strcpy(buffer, "--:");
1042
	}
1043
1044
	DEBUGString(dbgRenderer, x, y, buffer, colour);
1045
1046
	DEBUGNumber(x+3, y, addr, 4, colour);
1047
1048
}
1049
1050
static void
1051
DEBUGVAddress(int x, int y, int addr, SDL_Color colour)
1052
{
1053
	DEBUGNumber(x, y, addr, 5, colour);
1054
}