OXYGENHUB
# 2980b7b6d7cd enhanced-x16-emulator / src / debugger.c
66.2 KiB · C · last changed chore(): initial commit 25 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 "debug_server.h"
27
#include "dbg_info.h"
28
#include "rendertext.h"
29
30
static void DEBUGHandleKeyEvent(SDL_Keycode key,int isShift);
31
32
static void DEBUGNumber(int x,int y,int n,int w, SDL_Color colour);
33
static void DEBUGNumberDec(int x, int y, int n, int w, SDL_Color colour);
34
static void DEBUGAddress(int x, int y, int x16Bank, int addr, uint8_t bank, SDL_Color colour);
35
static void DEBUGVAddress(int x, int y, int addr, SDL_Color colour);
36
37
static void DEBUGRenderData(int y,uint32_t data);
38
static int DEBUGRenderZeroPageRegisters(int y);
39
static void DEBUGRenderVERAState(int y);
40
static int DEBUGRenderRegisters(void);
41
static void DEBUGRenderVRAM(int y, int data);
42
static void DEBUGRenderCode(int lines,int initialPC);
43
static void DEBUGRenderStack(int bytesCount);
44
static void DEBUGRenderCmdLine(int x, int width, int height);
45
static bool DEBUGEditCmdLine(SDL_Keycode key);
46
static void DEBUGBuildCmdLine(char *text);
47
static void DEBUGExecCmd();
48
49
// *******************************************************************************************
50
//
51
//		This is the minimum-interference flag. It's designed so that when
52
//		its non-zero DEBUGRenderDisplay() is called.
53
//
54
//			if (showDebugOnRender != 0) {
55
//				DEBUGRenderDisplay(SCREEN_WIDTH,SCREEN_HEIGHT,renderer);
56
//				SDL_RenderPresent(renderer);
57
//				return true;
58
//			}
59
//
60
//		before the SDL_RenderPresent call in video_update() in video.c
61
//
62
//		This controls what is happening. It is at the top of the main loop in main.c
63
//
64
//			if (isDebuggerEnabled != 0) {
65
//				int dbgCmd = DEBUGGetCurrentStatus();
66
//				if (dbgCmd > 0) continue;
67
//				if (dbgCmd < 0) break;
68
//			}
69
//
70
//		Both video.c and main.c require debugger.h to be included.
71
//
72
//		isDebuggerEnabled should be a flag set as a command line switch - without it
73
//		it will run unchanged. It should not be necessary to test the render code
74
//		because showDebugOnRender is statically initialised to zero and will only
75
//		change if DEBUGGetCurrentStatus() is called.
76
//
77
// *******************************************************************************************
78
79
//
80
//				0-9A-F sets the program address, with shift sets the data address.
81
//
82
#define DBGKEY_HOME     SDLK_F1                         // F1 is "Goto PC"
83
#define DBGKEY_RESET    SDLK_F2                         // F2 resets the 6502
84
#define DBGKEY_RUN      SDLK_F5                         // F5 is run.
85
#define DBGKEY_SETBRK   SDLK_F9                         // F9 sets breakpoint
86
#define DBGKEY_STEP     SDLK_F11                        // F11 is step into.
87
#define DBGKEY_STEPOVER SDLK_F10                        // F10 is step over.
88
#define DBGKEY_BANK_NEXT	SDLK_KP_PLUS
89
#define DBGKEY_BANK_PREV	SDLK_KP_MINUS
90
91
#define DBGSCANKEY_BRK  SDL_SCANCODE_F12                // F12 is break into running code.
92
#define DBGSCANKEY_SHOW SDL_SCANCODE_TAB                // Show screen key.
93
                                                        // *** MUST BE SCAN CODES ***
94
95
#define DBGMAX_ZERO_PAGE_REGISTERS 16
96
97
#define DDUMP_RAM	0
98
#define DDUMP_VERA	1
99
100
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' };
101
102
// RGB colours
103
const SDL_Color col_bkgnd= {0, 0, 0, 255};
104
const SDL_Color col_label= {0, 255, 0, 255};
105
const SDL_Color col_data= {0, 255, 255, 255};
106
const SDL_Color col_highlight= {255, 255, 0, 255};
107
const SDL_Color col_cmdLine= {255, 255, 255, 255};
108
109
const SDL_Color col_directpage= {192, 224, 255, 255};
110
111
const SDL_Color col_vram_tilemap = {0, 255, 255, 255};
112
const SDL_Color col_vram_tiledata = {0, 255, 0, 255};
113
const SDL_Color col_vram_special  = {255, 92, 92, 255};
114
const SDL_Color col_vram_other  = {128, 128, 128, 255};
115
116
int showDebugOnRender = 0;                            // Used to trigger rendering in video.c
117
int showFullDisplay = 0;                              // If non-zero show the whole thing.
118
int currentPC = -1;                                   // Current disassembly PC value (16-bit).
119
uint8_t currentPCBank = 0;                            // Current disassembly PC bank (like regs.k) value.
120
int currentData = 0;                                  // Current data display address (RAM or VRAM).
121
int currentPCX16Bank = -1;                            // Current disassembly X16 RAM/ROM bank, -1 unless $A000-$FFFF, .K = 0
122
int currentX16Bank = -1;                              // Current data display (memory dump) X16 RAM/ROM bank
123
int currentMode = DMODE_RUN;                          // Start running.
124
uint32_t debugCPUClocks = 0;
125
126
// Stepping by source line. Defined with the rest of the source-step machinery
127
// further down; declared here because DEBUGGetCurrentStatus(), which retires
128
// each instruction step, has to ask whether one is still in progress.
129
static bool srcStepActive;
130
static bool DEBUGSourceStepContinue(void);
131
132
int dumpmode          = DDUMP_RAM;
133
134
// Where we resumed from, so the breakpoint we are parked on does not fire again
135
// the instant we continue. Cleared once the CPU has actually moved on.
136
//
137
// Pressing F5 never needed this: the key is read inside DEBUGGetCurrentStatus,
138
// after the breakpoint test, so by the next call the CPU has already stepped
139
// off the address. Anything driving execution from outside -- which is the
140
// point of the execution-control functions -- changes the mode between calls
141
// instead, and would otherwise re-trigger the same breakpoint forever without
142
// ever advancing.
143
//
144
// The clock is part of the test, not just the address: `JMP *` at a breakpoint
145
// never changes the PC, and an address-only guard would then suppress that
146
// breakpoint permanently. Conversely the clock alone is not enough, in two
147
// ways: handle_ieee_intercept() can skip an iteration without executing
148
// anything, and -- the sharp one -- step6502() ticks the clock and returns
149
// immediately while the CPU is parked in WAI. A breakpoint on the instruction
150
// after a WAI, which is the idiomatic frame-sync placement, would otherwise
151
// clear the guard every poll and buy one emulated cycle per resume.
152
static int      resumeSkipPC    = -1;
153
static uint8_t  resumeSkipBank  = 0;
154
static uint32_t resumeSkipClock = 0;
155
156
// A watchpoint fired mid-instruction and the reported location still has to be
157
// taken, once the instruction that did the write has finished. See
158
// DEBUGBreakOnWatchpoint().
159
static bool watchpointStopPending = false;
160
161
static void DEBUGClearStepBreakPoint(void);
162
163
static void DEBUGArmResumeSkip(void) {
164
	resumeSkipPC    = regs.pc;
165
	resumeSkipBank  = regs.k;
166
	resumeSkipClock = clockticks6502;
167
}
168
169
struct breakpoint stepBreakPoint = { -1, 0, -1, 0, true };     // Single step break.
170
171
// Who asked for the pending step-over/step-out.
172
//
173
// The step target deliberately lives outside the shared breakpoint table, so it
174
// needs its own record of who wanted it -- and that record belongs next to the
175
// state it describes, rather than in a flag kept by whichever front end happened
176
// to start the step. DEBUG_OWNER_COUNT means nothing is pending.
177
static debug_owner_t stepOwner = DEBUG_OWNER_COUNT;
178
179
// Interrupt following. followInterrupts makes a step that is interrupted stop
180
// at the handler instead of running through it; breakOnInterrupt stops on every
181
// interrupt entry, even while free-running. Both default off so normal stepping
182
// behaves exactly as before.
183
static bool followInterrupts = false;
184
static bool breakOnInterrupt = false;
185
186
void DEBUGSetFollowInterrupts(bool on) { followInterrupts = on; }
187
bool DEBUGGetFollowInterrupts(void)    { return followInterrupts; }
188
void DEBUGSetBreakOnInterrupt(bool on) { breakOnInterrupt = on; }
189
bool DEBUGGetBreakOnInterrupt(void)    { return breakOnInterrupt; }
190
191
// Why execution last stopped. A UI needs this to tell a step apart from an
192
// arrival somewhere new: after a step the view should stay put, while a
193
// breakpoint or a pause has to re-centre or the highlight lands off-screen.
194
// Recorded here rather than in a debug client so it is correct whether or not
195
// one is attached. Only ever set to a string literal, so there is nothing to
196
// free and no buffer to overrun.
197
static const char *lastStopReason = "";
198
199
void
200
DEBUGSetStopReason(const char *reason)
201
{
202
	lastStopReason = reason ? reason : "";
203
}
204
205
// Record the reason AND tell a DAP client execution stopped.
206
//
207
// Split from the plain setter deliberately. Some sites run on every poll while
208
// already halted -- the F12 key test reads the physical key state, so it fires
209
// ~60-100 times a second while the key is held -- and others set a default that
210
// a more specific caller then overrides. Those must only store. Announcing is
211
// edge-triggered: exactly once per RUN->STOP transition, at the site that knows
212
// it is a transition. Folding the notify into the setter turned per-poll string
213
// assignments into per-poll socket writes.
214
//
215
// Compiles to a bare store in a build without the DAP server.
216
void
217
DEBUGAnnounceStop(const char *reason)
218
{
219
	DEBUGSetStopReason(reason);
220
	debug_server_notify_stopped(lastStopReason);
221
}
222
223
const char *
224
DEBUGGetStopReason(void)
225
{
226
	return lastStopReason;
227
}
228
char cmdLine[64]= "";                                 // command line buffer
229
int currentPosInLine= 0;                              // cursor position in the buffer (NOT USED _YET_)
230
int currentLineLen= 0;                                // command line buffer length
231
232
int    oldRegisters[DBGMAX_ZERO_PAGE_REGISTERS];      // Old ZP Register values, for change detection
233
char * oldRegChange[DBGMAX_ZERO_PAGE_REGISTERS];      // Change notification flags for output
234
int    oldRegisterTicks = 0;                          // Last PC when change notification was run
235
236
//
237
//		This flag controls
238
//
239
240
SDL_Renderer *dbgRenderer;                            // Renderer passed in.
241
242
// Unbanked, the RAM window, or the ROM window. A bank number selected for one
243
// of these means nothing in the others.
244
static int bank_window_of(int addr) {
245
	if (addr < 0xA000)
246
		return 0;
247
	return addr < 0xC000 ? 1 : 2;
248
}
249
// Which RAM/ROM window the PC is in, or -1 where a bank means nothing there.
250
// Delegates to debug_core so the rule that *produces* a selector is the same one
251
// the matcher applies. The copy that used to live here required the program bank
252
// to be zero unconditionally, which disagrees on gen1 with -c816: read6502
253
// forces the bank to zero there, so the window registers do apply.
254
static inline int getCurrentBank(int pc, uint8_t bank) {
255
	return debug_current_x16_bank(pc, bank);
256
}
257
258
// *******************************************************************************************
259
//
260
//      	This determines if we have hit a breakpoint, both in pc and bank
261
//
262
// *******************************************************************************************
263
264
static inline bool hitBreakpoint(int pc, uint8_t bank, struct breakpoint bp) {
265
	// Uses the same bank rule as every other matcher, so this field cannot mean
266
	// "any bank" in one place and "not banked" in another.
267
	return pc == bp.pc && bank == bp.bank
268
	       && debug_bank_selector_matches(bp.x16Bank, pc, bank);
269
}
270
271
// *******************************************************************************************
272
//
273
//			This is used to determine who is in control. If it returns zero then
274
//			everything runs normally till the next call.
275
//			If it returns +ve, then it will update the video, and loop round.
276
//			If it returns -ve, then exit.
277
//
278
// *******************************************************************************************
279
280
int  DEBUGGetCurrentStatus(void) {
281
282
	SDL_Event event;
283
	if (currentPC < 0) currentPC = regs.pc;                                      // Initialise current PC displayed.
284
285
	// Interrupt following. cpu_irq_take_entered_flag() is true exactly once per
286
	// interrupt entry, and at this point the PC is already the handler's first
287
	// instruction. "Break on interrupt" stops whenever one is taken; "follow
288
	// interrupts" only stops if we were stepping, so that an interrupt landing
289
	// in the middle of a step-over/step-out becomes visible instead of being
290
	// silently run through.
291
	//
292
	// The flag is consumed unconditionally -- reading it is what clears it -- so
293
	// that a stale entry cannot fire a stop later, after the feature is enabled.
294
	{
295
		const bool entered = cpu_irq_take_entered_flag();
296
		const bool wasStepping = (currentMode == DMODE_STEP) || (stepBreakPoint.pc >= 0);
297
		if (entered && (breakOnInterrupt
298
		                || (followInterrupts && wasStepping && currentMode != DMODE_STOP))) {
299
			currentPC = regs.pc;
300
			currentPCBank = regs.k;
301
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
302
			currentMode = DMODE_STOP;
303
			stepBreakPoint.pc = -1;                             // the pending step target is now unreachable
304
			stepBreakPoint.bank = 0;
305
			stepBreakPoint.x16Bank = -1;
306
			srcStepActive = false;   // and any step-by-line it was driving
307
			DEBUGAnnounceStop("interrupt");
308
		}
309
	}
310
311
	// A watchpoint stopped us from inside a store. The instruction that did the
312
	// write has now finished, so this is the first point at which regs.pc says
313
	// where the CPU actually ended up.
314
	if (watchpointStopPending) {
315
		watchpointStopPending = false;
316
		currentPC = regs.pc;
317
		currentPCBank = regs.k;
318
		currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
319
		// Reported here rather than from the store itself: only now does
320
		// regs.pc name the instruction after the write.
321
		DEBUGAnnounceStop("data breakpoint");
322
	}
323
324
	if (currentMode == DMODE_STEP) {                                // Single step before
325
		if (currentPC != regs.pc || currentPCBank != regs.k || currentPCX16Bank != getCurrentBank(regs.pc, regs.k)) { // Ensure that the PC moved
326
			currentPC = regs.pc;                                    // Update current PC
327
			currentPCBank = regs.k;
328
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);     // Update the bank if we are in upper memory.
329
			// A step by source line is this step repeated until the line
330
			// changes, so while it has not, arm the next one instead of
331
			// stopping. Announcing here and resuming would report a stop per
332
			// instruction to a DAP client for a step the user asked once for.
333
			if (!DEBUGSourceStepContinue()) {
334
				currentMode = DMODE_STOP;                               // So now stop, as we've done it.
335
				// A single step retires here without ever arming a step target, so
336
				// there is none to retract: the owner of a pending step is derived
337
				// from whether one is armed at all.
338
				DEBUGAnnounceStop("step");
339
			}
340
		}
341
	}
342
343
	// debug_bp_on_arrival() also counts the hit and spends the ignore budget,
344
	// so the DMODE_STOP guard is load-bearing rather than an optimisation: while
345
	// halted this function is polled continuously with the PC parked on the
346
	// breakpoint, and counting each poll would burn through an ignore count
347
	// while the machine is standing still.
348
	//
349
	// The resume guard covers the other half of the same problem: having just
350
	// been told to continue from a breakpoint, the PC is still sitting on it,
351
	// and testing it again would stop instantly and never make progress.
352
	// Cycles only count as progress when the CPU is actually running them: in
353
	// WAI it is parked, ticking the clock without retiring anything.
354
	const bool cpuAdvanced = (clockticks6502 != resumeSkipClock) && !waiting;
355
	if (resumeSkipPC >= 0
356
	    && (regs.pc != resumeSkipPC || regs.k != resumeSkipBank || cpuAdvanced)) {
357
		resumeSkipPC = -1;                                      // the CPU moved on; guard done
358
	}
359
	const bool stopped     = (currentMode == DMODE_STOP);
360
	const bool justResumed = (resumeSkipPC >= 0);
361
	// Parked in WAI counts as standing still here too. The CPU stops on the
362
	// instruction AFTER the WAI -- the opcode fetch has already advanced the PC
363
	// -- and then ticks the clock without retiring anything, so this poll runs
364
	// once per emulated cycle with the PC unchanged. Testing arrival there
365
	// stops before the awaited interrupt has happened, and because the test is
366
	// not a query -- it counts the hit and spends the ignore budget -- an
367
	// "ignore 60" meant as sixty frames is exhausted in sixty cycles, still
368
	// inside the first wait.
369
	//
370
	// Both ways out stay correct: a masked interrupt clears `waiting` without
371
	// vectoring, so the next poll sees the PC still on the breakpoint and stops
372
	// there; a vectored one arrives after the handler returns.
373
	if (!stopped && !justResumed && !waiting) {
374
		// Order matters: debug_bp_on_arrival() counts the hit and spends the
375
		// ignore budget, so it has to be reached exactly as often as before.
376
		const bool viaUser = debug_bp_on_arrival(regs.pc, regs.k);
377
		const bool viaStep = !viaUser && hitBreakpoint(regs.pc, regs.k, stepBreakPoint);
378
		if (viaUser || viaStep) {                               // Hit a breakpoint.
379
			currentPC = regs.pc;                                    // Update current PC
380
			currentPCBank = regs.k;
381
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);     // Update the bank if we are in upper memory.
382
			DEBUGClearStepBreakPoint();                             // and the step target is retired
383
			// A step-over that a source step is driving lands here every time
384
			// it runs over a call. Keep going while the line has not changed;
385
			// a breakpoint the user set always wins and ends the source step.
386
			bool keepStepping = false;
387
			if (viaUser)
388
				srcStepActive = false;
389
			else
390
				keepStepping = DEBUGSourceStepContinue();
391
			if (!keepStepping) {
392
				currentMode = DMODE_STOP;                           // So now stop, as we've done it.
393
				// A step-over or step-out finishing is a completed step, not a
394
				// breakpoint nobody set. Both the graphical debugger and a DAP
395
				// client key off this: a step leaves the view where it is, an
396
				// arrival re-centres it.
397
				DEBUGAnnounceStop(viaStep ? "step" : "breakpoint");
398
			}
399
		}
400
	}
401
402
	if (SDL_GetKeyboardState(NULL)[DBGSCANKEY_BRK]) {            // Stop on break pressed.
403
		const bool wasRunning = (currentMode != DMODE_STOP);
404
		currentMode = DMODE_STOP;
405
		srcStepActive = false;                                   // the user has taken over
406
		DEBUGSetStopReason("user");                              // store only: this test is level-triggered
407
		currentPC = regs.pc;                                     // Set the PC to what it is.
408
		currentPCBank = regs.k;
409
		currentPCX16Bank = getCurrentBank(regs.pc, regs.k);      // Update the bank if we are in upper memory.
410
		if (wasRunning) {                                        // Tell a DAP client too; see DEBUGBreakToDebugger().
411
			DEBUGAnnounceStop("pause");
412
		}
413
	}
414
415
	// Repair the bank when it is unset: currentPC moves independently of
416
	// regs.pc via the navigation keys, so it can land in a banked window while
417
	// currentPCX16Bank still says "none". Uses the shared rule rather than a
418
	// local copy of it, which is what let this one keep the old requirement
419
	// that the program bank be zero.
420
	if (currentPCX16Bank < 0) {
421
		currentPCX16Bank = debug_current_x16_bank(currentPC, currentPCBank);
422
	}
423
424
	// Who owns the UI while the machine is not running. With -imgui and no
425
	// text debugger the graphical one does: it pumps events, repaints, and
426
	// decides when to resume. Gated on the window having actually been created
427
	// -- if it failed to open there is no control bar to resume from, and
428
	// swallowing the loop here would park the machine unrecoverably.
429
	//
430
	// Tested on debug_window_enabled, not debugger_enabled: -debugport turns
431
	// debugger_enabled on by itself to get the breakpoint machinery, so keying
432
	// on it handed -imgui -debugport to the text debugger -- drawing its
433
	// overlay over a session that never asked for one, and swallowing the
434
	// emulator window's keys. debug_window_enabled means -debug/-bp/-wp, which
435
	// is the question being asked. (-imgui -bp still goes to the text pump, as
436
	// before: -bp does ask for it.)
437
	const bool imguiOwnsUI = (!debug_window_enabled && video_debug_ui_available());
438
439
	// A DAP client can drive the run state while we are halted, so give it a
440
	// turn before any front end blocks: continue/step arriving over the socket
441
	// has to be able to get us moving again, whichever UI is up.
442
	if (currentMode != DMODE_RUN) {
443
		debug_server_poll();
444
		if (currentMode == DMODE_RUN || currentMode == DMODE_STEP) {
445
			return 0;                                                   // Mode changed remotely — execute.
446
		}
447
	}
448
449
	// DMODE_STOP only, not "not RUN". DMODE_STEP means one instruction has been
450
	// asked for and must be executed now: routing it through the pump costs it
451
	// the pump's 16ms sleep, and a step stays in DMODE_STEP until the PC
452
	// actually moves. Over a WAI the PC does not move for the whole wait, so
453
	// stepping one instruction would run at ~60 emulated cycles a second and
454
	// take tens of minutes. The classic path below has the same shape: it only
455
	// sleeps once currentMode == DMODE_STOP.
456
	if (currentMode == DMODE_STOP && imguiOwnsUI) {
457
		showDebugOnRender = 0;                                  // no SDL overlay in this mode
458
		return video_debug_ui_pump_paused();
459
	}
460
461
	// The classic text debugger's event pump. Skipped entirely when the ImGui
462
	// window owns the UI: it drains the whole SDL queue into the text
463
	// debugger's key handler and drops everything else, so letting DMODE_STEP
464
	// fall in here destroys every mouse and keyboard event before ImGui or the
465
	// machine sees it. That is reachable whenever a step leaves the PC where it
466
	// was -- for the duration of a WAI, and permanently on a `JMP *` self-loop,
467
	// which would leave the debugger input-dead with no way to quit.
468
	if (currentMode != DMODE_RUN && !imguiOwnsUI) {                     // Not running, we own the keyboard.
469
		showFullDisplay =                                               // Check showing screen.
470
					SDL_GetKeyboardState(NULL)[DBGSCANKEY_SHOW];
471
		while (SDL_PollEvent(&event)) {                                 // We now poll events here.
472
			// With -debug and -imgui together this loop is the only one
473
			// running while halted -- video_update() returns early whenever
474
			// the text overlay is on screen -- so it has to feed the graphical
475
			// debugger too. Without this the ImGui window stayed on screen but
476
			// received no input at all: dead buttons, dead text fields, stale
477
			// mouse state. -imgui -bp reaches this combination without asking
478
			// for it, since -bp switches the text debugger on.
479
			if (video_debug_ui_available()) {
480
				video_debug_ui_feed_event(&event);
481
				// Events aimed at the debugger window must not also drive the
482
				// text debugger's command line.
483
				if (video_event_targets_debug_ui(&event)) {
484
					if (event.type == SDL_WINDOWEVENT &&
485
					    event.window.event == SDL_WINDOWEVENT_CLOSE) {
486
						continue;   // closing the debugger must not quit
487
					}
488
					video_debug_ui_shortcut_key(&event);
489
					continue;
490
				}
491
			}
492
			switch(event.type) {
493
				case SDL_QUIT:                                  // Time for exit
494
					return -1;
495
496
				case SDL_WINDOWEVENT:
497
					// With the ImGui window open SDL2 no longer synthesises
498
					// SDL_QUIT on a window close -- it only does that for the
499
					// last window -- so a close arrives here as a bare event.
500
					// Dropping it made the emulator window's [X] inert while
501
					// the text debugger was on screen under -debug -imgui.
502
					if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
503
						return -1;
504
					}
505
					break;
506
507
				case SDL_KEYDOWN:                               // Handle key presses.
508
					DEBUGHandleKeyEvent(event.key.keysym.sym, event.key.keysym.mod & (KMOD_LSHIFT|KMOD_RSHIFT));
509
					break;
510
511
				case SDL_TEXTINPUT:
512
					DEBUGBuildCmdLine(event.text.text);
513
					break;
514
			}
515
		}
516
	}
517
518
	showDebugOnRender = (currentMode != DMODE_RUN);                         // Do we draw it - only in RUN mode.
519
	if (currentMode == DMODE_STOP) {                                        // We're in charge.
520
		video_update();
521
		SDL_Delay(10);
522
		return 1;
523
	}
524
525
	// While running, a client still has to be able to connect and ask us to
526
	// break. This runs per instruction, so polling the socket every time would
527
	// cost more than the emulation; once every few thousand is still well
528
	// inside a frame.
529
	{
530
		static int poll_counter = 0;
531
		if (++poll_counter >= 1000) {
532
			poll_counter = 0;
533
			if (debug_server_poll()) {
534
				return (currentMode == DMODE_STOP) ? 1 : 0;
535
			}
536
		}
537
	}
538
539
	return 0;                                                               // Run wild, run free.
540
}
541
542
// *******************************************************************************************
543
//
544
//								Setup fonts and co
545
//
546
// *******************************************************************************************
547
void DEBUGInitUI(SDL_Renderer *pRenderer) {
548
		DEBUGInitChars(pRenderer);
549
		dbgRenderer = pRenderer;				// Save renderer.
550
}
551
552
// *******************************************************************************************
553
//
554
//								Setup fonts and co
555
//
556
// *******************************************************************************************
557
void DEBUGFreeUI() {
558
}
559
560
// *******************************************************************************************
561
//
562
//								Break into debugger from code.
563
//
564
// *******************************************************************************************
565
566
void DEBUGBreakToDebugger(void) {
567
	const bool wasRunning = (currentMode != DMODE_STOP);
568
	srcStepActive = false;   // whatever we were mid-way through, the user wins
569
	// Default reason for "something broke into the debugger". Set before the
570
	// mode changes, so no path can leave the previous stop's reason showing; a
571
	// caller with something more specific to say (the STP hook) overrides it
572
	// immediately after.
573
	DEBUGSetStopReason("user");   // store only; the transition notify is below
574
	currentMode = DMODE_STOP;
575
	currentPC = regs.pc;
576
	currentPCBank = regs.k;
577
	currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
578
	// A DAP client only refreshes its view -- stack, variables, step controls --
579
	// when it is told execution stopped. Halting without saying so leaves it
580
	// believing the machine is still running. Announced only on the transition,
581
	// so re-entering an already-halted debugger does not re-announce.
582
	if (wasRunning) {
583
		DEBUGAnnounceStop("pause");
584
	}
585
}
586
587
// A watched address was written. Called from the CPU store path, so it does as
588
// little as possible: memory.c has already established that a watchpoint
589
// matched.
590
//
591
// The reported location is captured later, not here. At store time the CPU is
592
// mid-instruction: the addressing mode has already advanced regs.pc past the
593
// operands, but the instruction's own effect on the PC has not happened yet. An
594
// ordinary STA lands where regs.pc already points, but a JSR pushing its return
595
// address, an interrupt pushing its frame, or MVN/MVP rewinding to repeat do
596
// not -- and those are exactly the writes a stack watchpoint is set to catch.
597
// Deferring the snapshot to the next status poll reports where the CPU
598
// actually ended up.
599
void DEBUGBreakOnWatchpoint(void) {
600
	if (currentMode != DMODE_STOP) {
601
		currentMode          = DMODE_STOP;
602
		watchpointStopPending = true;
603
	}
604
}
605
606
// *******************************************************************************************
607
//
608
//		Execution control.
609
//
610
//		One implementation, driven either by the debug window's function keys or
611
//		by another front end. Previously this logic lived only inside the key
612
//		handler, so anything else wanting to step had to synthesise key presses
613
//		-- and would drift out of step with it. Each resume re-bases the timing
614
//		so the emulator does not fast-forward to "catch up" on the time spent
615
//		halted.
616
//
617
// *******************************************************************************************
618
619
static void DEBUGClearStepBreakPoint(void) {
620
	// Whoever owned the pending step no longer does.
621
	stepOwner = DEBUG_OWNER_COUNT;
622
	stepBreakPoint.pc = -1;
623
	stepBreakPoint.bank = 0;
624
	stepBreakPoint.x16Bank = -1;
625
}
626
627
/* ---------------------------------------------------------------------------
628
 * Stepping by source line
629
 *
630
 * The machine steps by instruction, which is what the disassembly wants. One
631
 * line of C is many instructions, so in the Source panel a step key appeared to
632
 * do nothing for several presses running -- `total = 0;` alone is a call and
633
 * three more instructions.
634
 *
635
 * A source step is the instruction step repeated until the PC lands on a
636
 * DIFFERENT source line. Nothing about the underlying step changes: step-over
637
 * still runs calls to completion, step-into still enters them, and both still
638
 * stop on any breakpoint they run into on the way.
639
 * ------------------------------------------------------------------------- */
640
641
#define SRC_STEP_MAX_INSNS 500000   /* a line that never ends must not hang the UI */
642
643
/* srcStepActive is declared with the module state at the top: the instruction
644
 * step retires inside DEBUGGetCurrentStatus(), well before this point. */
645
static bool          srcStepOver    = false;
646
static debug_owner_t srcStepOwner   = DEBUG_OWNER_UI;
647
static char          srcStepFile[260];
648
static int           srcStepLine    = -1;
649
static uint16_t      srcStepSP      = 0;
650
static long          srcStepBudget  = 0;
651
static bool          sourceStepPref = true;
652
653
void DEBUGSetSourceStep(bool on) { sourceStepPref = on; }
654
bool DEBUGGetSourceStep(void)    { return sourceStepPref; }
655
656
/* The source line the PC is on, or false when the debug info does not cover it. */
657
static bool debug_line_at_pc(char *file, size_t size, int *line)
658
{
659
	const char *f = NULL;
660
	int         l = 0;
661
	if (!dbg_info_addr_to_source_banked(regs.pc, (int)memory_get_ram_bank(), &f, &l) || !f)
662
		return false;
663
	snprintf(file, size, "%s", f);
664
	*line = l;
665
	return true;
666
}
667
668
/* Stepping by line is only offered where a line means more than one
669
 * instruction. For assembly the two are usually the same thing, and quietly
670
 * running a whole macro expansion per keypress would change how every existing
671
 * project steps -- so it takes effect only for cc65 high-level (C) debug info. */
672
static bool debug_source_step_available(void)
673
{
674
	if (!sourceStepPref || !dbg_info_has_high_level())
675
		return false;
676
	char f[260];
677
	int  l = 0;
678
	return debug_line_at_pc(f, sizeof f, &l);
679
}
680
681
void DEBUGCancelSourceStep(void) { srcStepActive = false; }
682
683
static void DEBUGBeginSourceStep(bool over, debug_owner_t owner)
684
{
685
	srcStepActive = false;
686
	if (!debug_line_at_pc(srcStepFile, sizeof srcStepFile, &srcStepLine)) {
687
		if (over) DEBUGStepOver(owner); else DEBUGStepInto();
688
		return;
689
	}
690
	srcStepOver   = over;
691
	srcStepOwner  = owner;
692
	srcStepSP     = regs.sp;
693
	srcStepBudget = SRC_STEP_MAX_INSNS;
694
	srcStepActive = true;
695
	if (over) DEBUGStepOver(owner); else DEBUGStepInto();
696
}
697
698
/* Called where an instruction step has just retired. Returns true when the step
699
 * was one of ours and the PC has not reached a new line yet, having armed the
700
 * next one -- so the caller must NOT stop or announce. */
701
static bool DEBUGSourceStepContinue(void)
702
{
703
	if (!srcStepActive)
704
		return false;
705
706
	if (--srcStepBudget <= 0) {
707
		srcStepActive = false;   // give the UI back rather than spin forever
708
		return false;
709
	}
710
711
	char f[260];
712
	int  l = 0;
713
	if (!debug_line_at_pc(f, sizeof f, &l)) {
714
		// No source here. Stepping INTO a routine the debug info does not
715
		// describe -- the C runtime, the KERNAL -- is not somewhere to strand
716
		// the user one instruction at a time, so run to where it returns and
717
		// carry on from there. The stack having grown is what says we were
718
		// called rather than having jumped.
719
		if (regs.sp < srcStepSP) {
720
			DEBUGStepOut(srcStepOwner);
721
			return true;
722
		}
723
		srcStepActive = false;   // jumped somewhere unmapped; show it
724
		return false;
725
	}
726
727
	if (l == srcStepLine && strcmp(f, srcStepFile) == 0) {
728
		if (srcStepOver) DEBUGStepOver(srcStepOwner); else DEBUGStepInto();
729
		return true;
730
	}
731
732
	srcStepActive = false;       // a new line: this is where the step ends
733
	return false;
734
}
735
736
/* What the UI and DAP call: by line where that means anything, by instruction
737
 * otherwise. */
738
void DEBUGStepIntoAuto(void)
739
{
740
	if (debug_source_step_available())
741
		DEBUGBeginSourceStep(false, DEBUG_OWNER_UI);
742
	else
743
		DEBUGStepInto();
744
}
745
746
void DEBUGStepOverAuto(debug_owner_t owner)
747
{
748
	if (debug_source_step_available())
749
		DEBUGBeginSourceStep(true, owner);
750
	else
751
		DEBUGStepOver(owner);
752
}
753
754
// Public form of the same thing: a DAP session tearing down has to retract a
755
// step it started, and nothing outside this file can reach stepBreakPoint.
756
void DEBUGCancelStep(void) {
757
	srcStepActive = false;
758
	DEBUGClearStepBreakPoint();
759
}
760
761
debug_owner_t DEBUGStepOwner(void) {
762
	return stepBreakPoint.pc < 0 ? DEBUG_OWNER_COUNT : stepOwner;
763
}
764
765
// Retract a step only if `owner` is the one that started it.
766
//
767
// A session tearing down has to take back a step it asked for -- left armed, it
768
// stops the machine later for a client that has gone away -- but it must not
769
// take back one the user started at the keyboard. The server used to answer
770
// this from a flag of its own; the state being asked about lives here, so the
771
// question belongs here too.
772
bool DEBUGCancelStepFor(debug_owner_t owner) {
773
	if (DEBUGStepOwner() != owner)
774
		return false;
775
	DEBUGClearStepBreakPoint();
776
	return true;
777
}
778
779
// F5 — run until break.
780
//
781
// One deliberate difference from before: this abandons a pending step-over
782
// target, which the old F5 left armed. That target only survives at all if the
783
// step-over was interrupted by something other than reaching it -- F12, in
784
// practice -- and resuming afterwards would then stop at a return address the
785
// user had already broken out of and said nothing more about. Continuing means
786
// continuing; ask for the step again if that is what you wanted.
787
void DEBUGContinue(void) {
788
	srcStepActive = false;   // continuing ends any step-by-line in progress
789
	DEBUGClearStepBreakPoint();
790
	DEBUGArmResumeSkip();
791
	currentMode = DMODE_RUN;
792
	debugCPUClocks = clockticks6502;
793
	timing_init();
794
	// Last, so the run state and any step target are settled before a client
795
	// can see them: this can reach the socket, and a peer that has stopped
796
	// reading is dropped from inside it.
797
	debug_server_note_resumed();
798
}
799
800
void DEBUGStepInto(void) {                              // F11 — single instruction
801
	DEBUGArmResumeSkip();                               // step OFF a breakpoint, not into it again
802
	currentMode = DMODE_STEP;                           // runs once, then DEBUGGetCurrentStatus stops us
803
	currentPC = regs.pc;
804
	currentPCBank = regs.k;
805
	currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
806
	debugCPUClocks = clockticks6502;
807
	// Last, so the run state and any step target are settled before a client
808
	// can see them: this can reach the socket, and a peer that has stopped
809
	// reading is dropped from inside it.
810
	debug_server_note_resumed();
811
}
812
813
void DEBUGStepOver(debug_owner_t owner) {                // F10 — step over calls
814
	// Read the opcode through the bank live for the CURRENT pc. Using the
815
	// stale currentPCX16Bank (set when we last stopped) misreads the opcode
816
	// once the mapped bank has changed, so a JSR could be missed or invented.
817
	int x16Bank = getCurrentBank(regs.pc, regs.k);
818
	int opcode = debug_read6502(regs.pc, regs.k, x16Bank);
819
	// $22 and $FC are JSL and JSR (abs,X) only on the 65816. On a 65C02 they
820
	// are NOPs -- $22 a two-byte one -- so treating them as calls set the
821
	// return breakpoint past the following instruction and stepping over ran
822
	// away instead of stopping.
823
	const bool is_call = (opcode == 0x20)
824
	                  || (regs.is65c816 && (opcode == 0xFC || opcode == 0x22));
825
	if (is_call) {
826
		const int target = (regs.pc + 3 + (opcode == 0x22)) & 0xFFFF; // JSL is 4 bytes
827
		stepBreakPoint.pc = target;
828
		stepBreakPoint.bank = regs.k;
829
		// Derived from the target, not from the calling instruction. A call near
830
		// the top of the RAM window returns into the ROM window, where the
831
		// caller's RAM bank number selects nothing and the breakpoint could
832
		// never fire. Where the two are in the same window this is the same
833
		// answer, and it is what stepping out and running-to already do.
834
		stepBreakPoint.x16Bank = debug_current_x16_bank(target, regs.k);
835
		stepOwner = owner;
836
		DEBUGArmResumeSkip();
837
		currentMode = DMODE_RUN;
838
		debugCPUClocks = clockticks6502;
839
		timing_init();
840
	} else {                                            // not a call — same as step into
841
		DEBUGArmResumeSkip();
842
		currentMode = DMODE_STEP;
843
		currentPC = regs.pc;
844
		currentPCBank = regs.k;
845
		currentPCX16Bank = x16Bank;
846
		debugCPUClocks = clockticks6502;
847
	}
848
	// Last, so the run state and any step target are settled before a client
849
	// can see them: this can reach the socket, and a peer that has stopped
850
	// reading is dropped from inside it.
851
	debug_server_note_resumed();
852
}
853
854
void DEBUGStepOut(debug_owner_t owner) {                 // run to the return address
855
	// The return address is not necessarily on top of the stack: by the time
856
	// you want to step out, the routine has usually pushed registers or locals
857
	// over it. So scan upward for the first plausible return frame, keyed on
858
	// the call that must have created it -- a pushed 16-bit value V is a return
859
	// address if the byte at V-2 is JSR abs / JSR (abs,X), or at V-3 is JSL.
860
	// (Both push return-1, so execution resumes at V+1.)
861
	bool     native = regs.is65c816 && !regs.e;
862
	uint32_t sp     = regs.sp;
863
	uint32_t start  = native ? (sp + 1) : (0x100 + (sp & 0xFF) + 1);
864
	uint32_t end    = native ? (sp + 1 + 256) : 0x200;
865
866
	int     retAddr = -1;
867
	uint8_t retBank = regs.k;
868
	for (uint32_t a = start; a + 1 < end; a++) {
869
		uint8_t  lo     = debug_read6502((uint16_t)a, 0, -1);
870
		uint8_t  hi     = debug_read6502((uint16_t)(a + 1), 0, -1);
871
		uint16_t pushed = (uint16_t)(lo | (hi << 8));
872
873
		// $FC is JSR (abs,X) only on the 65816; on a 65C02 it is a NOP, and
874
		// accepting it there turns any pushed value preceded by one into a
875
		// false frame. Same gate as the one stepping over uses.
876
		uint8_t o2 = debug_read6502((uint16_t)(pushed - 2), regs.k, -1);
877
		if (o2 == 0x20 || (regs.is65c816 && o2 == 0xFC)) {   // JSR abs / JSR (abs,X)
878
			retAddr = (pushed + 1) & 0xFFFF;
879
			break;
880
		}
881
882
		// A JSL pushes the caller's program bank below the return address, so
883
		// the call it came from is in THAT bank, not the one we are stopped in.
884
		// Reading the opcode through regs.k would inspect whatever happens to
885
		// sit at that address in the callee's bank -- which for a long call is
886
		// exactly the bank that is wrong.
887
		// A long call only exists on the 65816 at all.
888
		if (regs.is65c816 && a + 2 < end) {
889
			uint8_t callerBank = debug_read6502((uint16_t)(a + 2), 0, -1);
890
			if (debug_read6502((uint16_t)(pushed - 3), callerBank, -1) == 0x22) {   // JSL
891
				retAddr = (pushed + 1) & 0xFFFF;
892
				retBank = callerBank;
893
				break;
894
			}
895
		}
896
	}
897
898
	if (retAddr < 0) {
899
		// No recognisable frame — a hand-rolled stack, or we are not inside a
900
		// call at all. Single-step so the caller still makes progress.
901
		DEBUGStepInto();
902
		return;
903
	}
904
905
	stepBreakPoint.pc = retAddr;
906
	stepBreakPoint.bank = retBank;
907
	stepBreakPoint.x16Bank = getCurrentBank(stepBreakPoint.pc, retBank);
908
	stepOwner = owner;
909
	DEBUGArmResumeSkip();
910
	currentMode = DMODE_RUN;
911
	debugCPUClocks = clockticks6502;
912
	timing_init();
913
	// Last, so the run state and any step target are settled before a client
914
	// can see them: this can reach the socket, and a peer that has stopped
915
	// reading is dropped from inside it.
916
	debug_server_note_resumed();
917
}
918
919
void DEBUGPause(void) {
920
	DEBUGBreakToDebugger();   // records "user"
921
}
922
923
void DEBUGRunTo(uint16_t pc, uint8_t bank, debug_owner_t owner) {
924
	srcStepActive = false;   // an explicit destination ends any step-by-line
925
	stepBreakPoint.pc = pc;
926
	stepBreakPoint.bank = bank;
927
	stepBreakPoint.x16Bank = getCurrentBank(pc, bank);
928
	stepOwner = owner;
929
	DEBUGArmResumeSkip();
930
	currentMode = DMODE_RUN;
931
	debugCPUClocks = clockticks6502;
932
	timing_init();
933
	// Last, so the run state and any step target are settled before a client
934
	// can see them: this can reach the socket, and a peer that has stopped
935
	// reading is dropped from inside it.
936
	debug_server_note_resumed();
937
}
938
939
bool DEBUGIsRunning(void) {
940
	return currentMode == DMODE_RUN;
941
}
942
943
bool DEBUGIsPaused(void) {
944
	return currentMode == DMODE_STOP;
945
}
946
947
948
// *******************************************************************************************
949
//
950
//									Handle keyboard state.
951
//
952
// *******************************************************************************************
953
954
static void DEBUGHandleKeyEvent(SDL_Keycode key, int isShift) {
955
956
	switch(key) {
957
958
		case DBGKEY_STEP:      // Single step (F11 by default)
959
			if (isShift) {
960
				DEBUGStepOut(DEBUG_OWNER_UI);
961
			} else {
962
				DEBUGStepInto();
963
			}
964
			break;
965
966
		case DBGKEY_STEPOVER:  // Step over (F10 by default)
967
			DEBUGStepOver(DEBUG_OWNER_UI);
968
			break;
969
970
		case DBGKEY_RUN:                                // F5 Runs until Break.
971
			DEBUGContinue();
972
			break;
973
974
		case DBGKEY_SETBRK:                             // F9 Set breakpoint to displayed.
975
			// Now a toggle over the table rather than over one slot, so F9 adds
976
			// a breakpoint without silently discarding the previous one.
977
			//
978
			// Deleting takes the breakpoint away whoever asked for it, rather
979
			// than merely dropping this debugger's own claim on it. Someone at
980
			// the keyboard pressing F9 on a breakpoint a DAP client happens to
981
			// have set expects it gone, not to watch the key do nothing because
982
			// an editor somewhere else still wants it.
983
			debug_bp_toggle_for(currentPC, (uint8_t)currentPCBank, currentPCX16Bank,
984
			                    DEBUG_OWNER_UI);
985
			break;
986
987
		case DBGKEY_HOME:                               // F1 sets the display PC to the actual one.
988
			currentPC = regs.pc;
989
			currentPCBank = regs.k;
990
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
991
			break;
992
993
		case DBGKEY_RESET:                              // F2 reset the 6502
994
			reset6502(regs.is65c816);
995
			currentPC = regs.pc;
996
			currentPCBank = regs.k;
997
			// Derive the window bank like every other handler. The reset vector
998
			// puts the PC in ROM, so hardcoding "no bank" here left the display
999
			// disagreeing with the machine -- and F9 would then arm a
1000
			// breakpoint for every ROM bank rather than the one on screen.
1001
			currentPCX16Bank = getCurrentBank(regs.pc, regs.k);
1002
			break;
1003
1004
		case DBGKEY_BANK_NEXT:
1005
			currentX16Bank += 1;
1006
			break;
1007
1008
		case DBGKEY_BANK_PREV:
1009
			currentX16Bank -= 1;
1010
			break;
1011
1012
		case SDLK_PAGEDOWN:
1013
			if (isShift) {
1014
				currentPC = (currentPC + 0x10) & 0xFFFF;
1015
				if (currentPCBank == 0 && currentPC < 0xA000) {
1016
					currentPCX16Bank = -1;
1017
				}
1018
			} else {
1019
				if (dumpmode == DDUMP_RAM) {
1020
					currentData = (currentData + 0x100) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
1021
				} else {
1022
					currentData = (currentData + 0x200) & 0x1FFFF;
1023
				}
1024
			}
1025
			break;
1026
1027
		case SDLK_PAGEUP:
1028
			if (isShift) {
1029
				currentPC = (currentPC - 0x10) & 0xFFFF;
1030
				if (currentPCBank == 0 && currentPC < 0xA000) {
1031
					currentPCX16Bank = -1;
1032
				}
1033
			} else {
1034
				if (dumpmode == DDUMP_RAM) {
1035
					currentData = (currentData - 0x100) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
1036
				} else {
1037
					currentData = (currentData - 0x200) & 0x1FFFF;
1038
				}
1039
			}
1040
			break;
1041
1042
		case SDLK_DOWN:
1043
			if (isShift) {
1044
				currentPC = (currentPC + 1) & 0xFFFF;
1045
				if (currentPCBank == 0 && currentPC < 0xA000) {
1046
					currentPCX16Bank = -1;
1047
				}
1048
			} else {
1049
				if (dumpmode == DDUMP_RAM) {
1050
					currentData = (currentData + 0x08) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
1051
				} else {
1052
					currentData = (currentData + 0x10) & 0x1FFFF;
1053
				}
1054
			}
1055
			break;
1056
1057
		case SDLK_UP:
1058
			if (isShift) {
1059
				currentPC = (currentPC - 1) & 0xFFFF;
1060
				if (currentPCBank == 0 && currentPC < 0xA000) {
1061
					currentPCX16Bank = -1;
1062
				}
1063
			} else {
1064
				if (dumpmode == DDUMP_RAM) {
1065
					currentData = (currentData - 0x08) & (is_gen2 ? 0xFFFFFF : 0xFFFF);
1066
				} else {
1067
					currentData = (currentData - 0x10) & 0x1FFFF;
1068
				}
1069
			}
1070
			break;
1071
1072
		default:
1073
			if(DEBUGEditCmdLine(key)) {
1074
				// printf("cmd line: %s\n", cmdLine);
1075
				DEBUGExecCmd();
1076
			}
1077
			break;
1078
	}
1079
1080
}
1081
1082
static void DEBUGBuildCmdLine(char *text) {
1083
	uint8_t *ptr = (uint8_t *)text;
1084
	while(currentLineLen < sizeof(cmdLine)-1 && *ptr != 0) {
1085
		if (*ptr < 0x80) { // We only care about characters in ASCII range.
1086
		                   // UTF-8 encodings will always have the high bit
1087
		                   // set for any byte which is part of a multibyte
1088
		                   // character, so we need not parse it whatsoever.
1089
			uint8_t key = *ptr;
1090
1091
			if (key >= 0x20 && key <= 0x7D) {
1092
				if (key >= 'A' && key <= 'Z') {
1093
					key += 0x20; // case-fold to lower
1094
				}
1095
				cmdLine[currentPosInLine++]= key;
1096
				if(currentPosInLine > currentLineLen) {
1097
					currentLineLen++;
1098
					cmdLine[currentLineLen]= 0;
1099
				}
1100
			}
1101
		}
1102
		ptr++;
1103
	}
1104
}
1105
1106
static bool DEBUGEditCmdLine(SDL_Keycode key) {
1107
	// right now, let's have a rudimentary input: only backspace to delete last char
1108
	// later, I want a real input line with delete, backspace, left and right cursor
1109
	// devs like their comfort ;)
1110
	if(key == SDLK_BACKSPACE) {
1111
		currentPosInLine--;
1112
		if(currentPosInLine<0) {
1113
			currentPosInLine= 0;
1114
		}
1115
		currentLineLen--;
1116
		if(currentLineLen<0) {
1117
			currentLineLen= 0;
1118
		}
1119
		cmdLine[currentLineLen]= 0;
1120
	}
1121
	return (key == SDLK_RETURN) || (key == SDLK_KP_ENTER);
1122
}
1123
1124
static void DEBUGExecCmd() {
1125
	int number, bnumber, addr, size, incr;
1126
	char reg[10];
1127
	char cmd;
1128
	char *line= ltrim(cmdLine);
1129
1130
	cmd= *line;
1131
	if(*line) {
1132
		line++;
1133
	}
1134
	// printf("cmd:%c line: '%s'\n", cmd, line);
1135
1136
	switch (cmd) {
1137
		case CMD_DUMP_MEM:
1138
			if (sscanf(line, "%x:%x", &bnumber, &number) == 2) {
1139
				currentX16Bank = bnumber & 0xFF;
1140
			} else if (sscanf(line, "%x", &number) == 1) {
1141
				if (!is_gen2) {
1142
					currentX16Bank = (number >> 16) & 0xFF;
1143
				}
1144
			} else {
1145
				break;
1146
			}
1147
			addr = number & (is_gen2 ? 0xFFFFFF : 0xFFFF);
1148
			currentData = addr;
1149
			dumpmode    = DDUMP_RAM;
1150
			break;
1151
1152
		case CMD_DUMP_VERA:
1153
			if (sscanf(line, "%x", &number) == 1) {
1154
				addr = number & 0x1FFFF;
1155
				currentData = addr;
1156
				dumpmode    = DDUMP_VERA;
1157
			}
1158
			break;
1159
1160
		case CMD_FILL_MEMORY:
1161
			size = 1;
1162
			incr = 1;
1163
			if (sscanf(line, "%x %x %d %d", &addr, &number, &size, &incr) >= 2) {
1164
				if (dumpmode == DDUMP_RAM) {
1165
					addr &= 0xFFFFFF;
1166
					do {
1167
						if (addr >= 0xC000 && addr < 0x10000) {
1168
							// Nop.
1169
						} else if (addr >= 0xA000 && addr < 0xC000) {
1170
							// -1 means "whichever bank is mapped", as it does
1171
							// everywhere else this is used, so resolve it before
1172
							// bounds-checking rather than rejecting it -- the
1173
							// initial value is -1 and refusing it would make the
1174
							// commonest case silently do nothing.
1175
							//
1176
							// Bounds-checked like the RAM branch below, which it
1177
							// was not: the bank is taken from user input with
1178
							// only a & 0xFF, so an out-of-range one indexed past
1179
							// the end of the allocation, and the unset -1 indexed
1180
							// before its start.
1181
							const int fill_bank = currentX16Bank < 0
1182
							                          ? (int)memory_get_ram_bank()
1183
							                          : currentX16Bank;
1184
							if (fill_bank >= 0 && fill_bank < (int)num_ram_banks) {
1185
								BRAM[(fill_bank << 13) + addr - 0xA000] = number;
1186
							}
1187
						} else if ((addr >> 16) < num_banks) {
1188
							RAM[addr] = number;
1189
						}
1190
						if (incr) {
1191
							addr += incr;
1192
						} else {
1193
							++addr;
1194
						}
1195
						addr &= 0xFFFFFF;
1196
						--size;
1197
					} while (size > 0);
1198
				} else {
1199
					addr &= 0x1FFFF;
1200
					do {
1201
						video_space_write(addr, number);
1202
						if (incr) {
1203
							addr += incr;
1204
						} else {
1205
							++addr;
1206
						}
1207
						addr &= 0x1FFFF;
1208
						--size;
1209
					} while (size > 0);
1210
				}
1211
			}
1212
			break;
1213
1214
		case CMD_DISASM:
1215
			if (sscanf(line, "%x:%x", &bnumber, &number) == 2) {
1216
				currentPCX16Bank = (number >= 0xA000 && number <= 0xFFFF) ? bnumber & 0xFF : -1;
1217
			} else if (sscanf(line, "%x", &number) == 1) {
1218
				if (!is_gen2) {
1219
					currentPCX16Bank = (number & 0xFFFF) >= 0xA000 ? (number >> 16) & 0xFF : -1;
1220
				} else if (number < 0x00A000 || number >= 0x010000) { // treat input as 24-bit (K+PC)
1221
					currentPCX16Bank = -1; // if outside of the X16 banked range, set to undef (-1)
1222
				}
1223
			} else {
1224
				break;
1225
			}
1226
1227
			addr = number & (is_gen2 ? 0xFFFFFF : 0xFFFF);
1228
1229
			currentPC = addr & 0xFFFF;
1230
			if (is_gen2) {
1231
				currentPCBank = (addr >> 16) & 0xFF;
1232
			}
1233
			break;
1234
1235
		case CMD_SET_BANK:
1236
			if (sscanf(line, "%s %d", reg, &number) != 2) {
1237
				break;
1238
			}
1239
1240
			if (!strcmp(reg, "rom")) {
1241
				memory_set_rom_bank(number & 0x00FF);
1242
			} else if (!strcmp(reg, "ram")) {
1243
				memory_set_ram_bank(number & 0x00FF);
1244
			}
1245
			break;
1246
1247
		case CMD_SET_REGISTER:
1248
			if (sscanf(line, "%s %x", reg, &number) != 2) {
1249
				break;
1250
			}
1251
1252
			if(!strcmp(reg, "pc")) {
1253
				regs.pc= number & 0xFFFF;
1254
				waiting = 0;
1255
			}
1256
			if(!strcmp(reg, "a")) {
1257
				regs.a= number & 0x00FF;
1258
			}
1259
			if(!strcmp(reg, "b")) {
1260
				regs.b= number & 0x00FF;
1261
			}
1262
			if(!strcmp(reg, "c")) {
1263
				regs.c= number & 0xFFFF;
1264
			}
1265
			if(!strcmp(reg, "d")) {
1266
				regs.dp= number & 0xFFFF;
1267
			}
1268
			if(!strcmp(reg, "k")) {
1269
				regs.k= number & 0x00FF;
1270
			}
1271
			if(!strcmp(reg, "dbr")) {
1272
				regs.db= number & 0x00FF;
1273
			}
1274
			if(!strcmp(reg, "x")) {
1275
				if (regs.e) {
1276
					regs.xl= number & 0x00FF;
1277
				} else {
1278
					regs.x= number & 0xFFFF;
1279
				}
1280
			}
1281
			if(!strcmp(reg, "y")) {
1282
				if (regs.e) {
1283
					regs.yl= number & 0x00FF;
1284
				} else {
1285
					regs.y= number & 0xFFFF;
1286
				}
1287
			}
1288
			if(!strcmp(reg, "sp")) {
1289
				if (regs.e) {
1290
					regs.sp= 0x100 | (number & 0x00FF);
1291
				} else {
1292
					regs.sp= number & 0xFFFF;
1293
				}
1294
			}
1295
			break;
1296
1297
		default:
1298
			break;
1299
	}
1300
1301
	currentPosInLine= currentLineLen= *cmdLine= 0;
1302
}
1303
1304
// *******************************************************************************************
1305
//
1306
//							Render the emulator debugger display.
1307
//
1308
// *******************************************************************************************
1309
1310
void DEBUGRenderDisplay(int width, int height) {
1311
	if (showFullDisplay) return;								// Not rendering debug.
1312
1313
	SDL_Rect rc;
1314
	rc.w = DBG_WIDTH * 6 * CHAR_SCALE;							// Erase background, set up rect
1315
	rc.h = height;
1316
	xPos = width-rc.w;yPos = 0; 								// Position screen
1317
	rc.x = xPos;rc.y = yPos; 									// Set rectangle and black out.
1318
	SDL_SetRenderDrawColor(dbgRenderer,0,0,0,SDL_ALPHA_OPAQUE);
1319
	SDL_RenderFillRect(dbgRenderer,&rc);
1320
1321
	int register_lines = DEBUGRenderRegisters();							// Draw register name and values.
1322
	DEBUGRenderCode(register_lines, currentPC);							// Render 6502 disassembly.
1323
	if (dumpmode == DDUMP_RAM) {
1324
		DEBUGRenderData(register_lines + 1, currentData);
1325
		int zp_lines = DEBUGRenderZeroPageRegisters(register_lines + 1);
1326
		DEBUGRenderVERAState(zp_lines + 1);
1327
	} else {
1328
		DEBUGRenderVRAM(register_lines + 1, currentData);
1329
	}
1330
	DEBUGRenderStack(register_lines);
1331
1332
	DEBUGRenderCmdLine(xPos, rc.w, height);
1333
}
1334
1335
// *******************************************************************************************
1336
//
1337
//									 Render command Line
1338
//
1339
// *******************************************************************************************
1340
1341
static void DEBUGRenderCmdLine(int x, int width, int height) {
1342
	char buffer[sizeof(cmdLine)+1];
1343
1344
	SDL_SetRenderDrawColor(dbgRenderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
1345
	SDL_RenderDrawLine(dbgRenderer, x, height-12, x+width, height-12);
1346
1347
	sprintf(buffer, ">%s", cmdLine);
1348
	DEBUGString(dbgRenderer, 0, DBG_HEIGHT-1, buffer, col_cmdLine);
1349
	DEBUGString(dbgRenderer, currentPosInLine+1, DBG_HEIGHT-1, "_", col_cmdLine); // crude cursor
1350
}
1351
1352
// *******************************************************************************************
1353
//
1354
//									 Render Zero Page Registers
1355
//
1356
// *******************************************************************************************
1357
1358
static int DEBUGRenderZeroPageRegisters(int y) {
1359
#define LAST_R 15
1360
	unsigned char reg = 0;
1361
	int y_start = y;
1362
	char lbl[12];
1363
	while (reg < DBGMAX_ZERO_PAGE_REGISTERS) {
1364
		if (((y-y_start) % 5) != 0) {           // Break registers into groups of 5, easier to locate
1365
			if (reg <= LAST_R)
1366
				sprintf(lbl, "R%d", reg);
1367
			else
1368
				sprintf(lbl, "x%d", reg);
1369
1370
			DEBUGString(dbgRenderer, DBG_ZP_REG, y, lbl, col_label);
1371
1372
			int reg_addr = 2 + reg * 2;
1373
			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);
1374
1375
			DEBUGNumber(DBG_ZP_REG+5, y, n, 4, col_data);
1376
1377
			if (oldRegChange[reg] != NULL)
1378
				DEBUGString(dbgRenderer, DBG_ZP_REG+9, y, oldRegChange[reg], col_data);
1379
1380
			if (oldRegisterTicks != clockticks6502) {   // change detection only when the emulated CPU changes
1381
				oldRegChange[reg] = n != oldRegisters[reg] ? "*" : " ";
1382
				oldRegisters[reg]=n;
1383
			}
1384
			reg++;
1385
		}
1386
		y++;
1387
	}
1388
1389
	if (oldRegisterTicks != clockticks6502) {
1390
		oldRegisterTicks = clockticks6502;
1391
	}
1392
1393
	return y;
1394
}
1395
1396
// *******************************************************************************************
1397
//
1398
//									 Render Data Area
1399
//
1400
// *******************************************************************************************
1401
1402
static void DEBUGRenderData(int y,uint32_t data) {
1403
	while (y < DBG_HEIGHT-2) {									// To bottom of screen
1404
		DEBUGAddress(DBG_MEMX, y, (uint8_t)currentX16Bank, data & 0xFFFF, data >> 16, col_label);	// Show label.
1405
1406
		for (int i = 0;i < 8;i++) {
1407
			bool isDP = (data >> 16) == 0 && ((data+i - regs.dp) & 0xffff) < 256;
1408
			int byte = debug_read6502((data+i) & 0xFFFF, data >> 16, currentX16Bank);
1409
			DEBUGNumber(DBG_MEMX+8+i*3,y,byte,2, isDP ? col_directpage : col_data);
1410
			DEBUGWrite(dbgRenderer, DBG_MEMX+33+i,y,byte, isDP ? col_directpage : col_data);
1411
		}
1412
		y++;
1413
		data = (data + 8) & 0xFFFFFF;
1414
	}
1415
}
1416
1417
static void DEBUGRenderVRAM(int y, int data) {
1418
	while (y < DBG_HEIGHT - 2) {                                                   // To bottom of screen
1419
		DEBUGVAddress(DBG_MEMX, y, data & 0x1FFFF, col_label); // Show label.
1420
1421
		for (int i = 0; i < 16; i++) {
1422
			int addr = (data + i) & 0x1FFFF;
1423
			int byte = video_space_read(addr);
1424
1425
			if (video_is_tilemap_address(addr)) {
1426
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_tilemap);
1427
			} else if (video_is_tiledata_address(addr)) {
1428
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_tiledata);
1429
			} else if (video_is_special_address(addr)) {
1430
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_special);
1431
			} else {
1432
				DEBUGNumber(DBG_MEMX + 6 + i * 3, y, byte, 2, col_vram_other);
1433
			}
1434
		}
1435
		y++;
1436
		data += 16;
1437
	}
1438
}
1439
1440
// *******************************************************************************************
1441
//
1442
//									 Render Disassembly
1443
//
1444
// *******************************************************************************************
1445
1446
1447
static void DEBUGRenderCode(int lines, int initialPC) {
1448
	char buffer[32];
1449
	uint8_t implied_status = regs.status;
1450
	uint8_t implied_e = regs.e;
1451
	uint8_t opcode, operand, carry;
1452
	int implied_x16_bank = currentPCX16Bank;
1453
	// Which window that bank belongs to. A bank number only means something
1454
	// inside the window it names -- a RAM bank does not select a ROM bank -- so
1455
	// when the walk crosses out of it the carried value is re-derived from what
1456
	// is mapped there instead of being carried on regardless.
1457
	int implied_window = bank_window_of(initialPC);
1458
1459
	for (int y = 0; y < lines; y++) { 							// Each line
1460
		DEBUGAddress(DBG_ASMX, y, implied_x16_bank, initialPC, currentPCBank, col_label);
1461
		int32_t eff_addr;
1462
1463
		// Attempt to display the disassembly correctly more often
1464
		// if the code logic is reasonably straightforward with respect
1465
		// to status flags changing in the immediate instruction list
1466
1467
		// This doesn't predict status flags changes except by the few
1468
		// opcodes below. The PLP instruction, for instance, could easily
1469
		// render disassembly following it invalid, but this would have
1470
		// still been true without the added logic, anyway.
1471
1472
		if (regs.is65c816) {
1473
			// Read through the bank this line is labelled with, not the one the
1474
			// pane started in. This scan predicts the M/X widths, which decide
1475
			// how many bytes the next immediate instruction occupies -- so
1476
			// reading it from a different bank than the one being disassembled
1477
			// mis-sizes the instruction and every line after it lands at the
1478
			// wrong offset.
1479
			opcode = debug_read6502(initialPC, currentPCBank, implied_x16_bank);
1480
			switch (opcode) {
1481
				case 0x81: // CLC
1482
					implied_status &= ~FLAG_CARRY;
1483
					;;
1484
				case 0x83: // SEC
1485
					implied_status |= FLAG_CARRY;
1486
					;;
1487
				case 0xC2: // REP
1488
					operand = debug_read6502((initialPC+1) & 0xffff, currentPCBank, implied_x16_bank);
1489
					implied_status = ~operand & implied_status;
1490
					;;
1491
				case 0xE2: // SEP
1492
					operand = debug_read6502((initialPC+1) & 0xffff, currentPCBank, implied_x16_bank);
1493
					implied_status = operand | implied_status;
1494
					;;
1495
				case 0xFB: // XCE
1496
					carry = implied_status & FLAG_CARRY;
1497
					implied_status = (implied_status & ~FLAG_CARRY) | (implied_e ? FLAG_CARRY : 0);
1498
					implied_e = carry != 0;
1499
					;;
1500
				default:
1501
					;;
1502
			}
1503
			if (implied_e) implied_status |= FLAG_INDEX_WIDTH | FLAG_MEMORY_WIDTH;
1504
1505
		}
1506
		int size = disasm(initialPC, currentPCBank, RAM, buffer, sizeof(buffer), implied_x16_bank, implied_status, &eff_addr);	// Disassemble code
1507
		// Output assembly highlighting PC
1508
		DEBUGString(dbgRenderer, DBG_ASMX+8, y, buffer, initialPC == regs.pc ? col_highlight : col_data);
1509
		// Populate effective address
1510
		if (initialPC == regs.pc) {
1511
			if (eff_addr < 0) {
1512
				DEBUGString(dbgRenderer, DBG_DATX, lines-1, "----", col_data);
1513
			} else {
1514
				DEBUGNumber(DBG_DATX, lines-1, eff_addr, 4, col_data);
1515
			}
1516
		}
1517
		initialPC = (initialPC + size) & 0xFFFF;										// Forward to next
1518
		// Re-derive on leaving the window the carried bank belongs to. Without
1519
		// this the bank the pane started with is used for every line, so a walk
1520
		// from the RAM window into the ROM window selects a ROM bank by its RAM
1521
		// bank number and renders bytes from the wrong one.
1522
		if (bank_window_of(initialPC) != implied_window) {
1523
			implied_window   = bank_window_of(initialPC);
1524
			implied_x16_bank = debug_current_x16_bank(initialPC, currentPCBank);
1525
		}
1526
	}
1527
}
1528
1529
// *******************************************************************************************
1530
//
1531
//									Render Register Display
1532
//
1533
// *******************************************************************************************
1534
1535
static char *labels_c816[] = { "NVMXDIZCE","","","A","B","C","X","Y","K","DB","","PC","DP","SP","BKA","BKO","","BRK","EFF", NULL };
1536
static char *labels_c02[] = { "NV-BDIZC","","","A","X","Y","","PC","SP","BKA","BKO","","BRK","EFF", NULL };
1537
1538
static void DEBUGNumberHighByteCondition(int x, int y, int n, bool condition, SDL_Color ifTrue, SDL_Color ifFalse) {
1539
	if (condition) {
1540
		DEBUGNumber(x, y, n >> 8, 2, ifTrue);
1541
		DEBUGNumber(x + 2, y, n & 0xFF, 2, ifFalse);
1542
	} else {
1543
		DEBUGNumber(x, y, n, 4, ifFalse);
1544
	}
1545
}
1546
1547
static int DEBUGRenderRegisters(void) {
1548
	int n = 0,yc = 0;
1549
	if (regs.is65c816) {
1550
		while (labels_c816[n] != NULL) {								// Labels
1551
			DEBUGString(dbgRenderer, DBG_LBLX,n,labels_c816[n], col_label);n++;
1552
		}
1553
		yc++;
1554
		DEBUGNumber(DBG_LBLX, yc, (regs.status >> 7) & 1, 1, col_data);
1555
		DEBUGNumber(DBG_LBLX+1, yc, (regs.status >> 6) & 1, 1, col_data);
1556
		DEBUGNumber(DBG_LBLX+2, yc, (regs.status >> 5) & 1, 1, regs.e ? col_vram_other : col_data);
1557
		DEBUGNumber(DBG_LBLX+3, yc, (regs.status >> 4) & 1, 1, regs.e ? col_vram_other : col_data);
1558
		DEBUGNumber(DBG_LBLX+4, yc, (regs.status >> 3) & 1, 1, col_data);
1559
		DEBUGNumber(DBG_LBLX+5, yc, (regs.status >> 2) & 1, 1, col_data);
1560
		DEBUGNumber(DBG_LBLX+6, yc, (regs.status >> 1) & 1, 1, col_data);
1561
		DEBUGNumber(DBG_LBLX+7, yc, (regs.status >> 0) & 1, 1, col_data);
1562
		DEBUGNumber(DBG_LBLX+8, yc, regs.e, 1, col_data);
1563
		yc+= 2;
1564
1565
		DEBUGNumber(DBG_DATX, yc++, regs.a, 2, col_data);
1566
		DEBUGNumber(DBG_DATX, yc++, regs.b, 2, col_data);
1567
		DEBUGNumber(DBG_DATX, yc++, regs.c, 4, col_data);
1568
		DEBUGNumberHighByteCondition(DBG_DATX, yc++, regs.x, (regs.status >> 4) & 1, col_vram_other, col_data);
1569
		DEBUGNumberHighByteCondition(DBG_DATX, yc++, regs.y, (regs.status >> 4) & 1, col_vram_other, col_data);
1570
		DEBUGNumber(DBG_DATX, yc++, regs.k, 2, col_data);
1571
		DEBUGNumber(DBG_DATX, yc++, regs.db, 2, col_data);
1572
		yc++;
1573
1574
		DEBUGNumber(DBG_DATX, yc++, regs.pc, 4, col_data);
1575
		DEBUGNumber(DBG_DATX, yc++, regs.dp, 4, col_data);
1576
		DEBUGNumberHighByteCondition(DBG_DATX, yc++, regs.sp, regs.e, col_vram_other, col_data);
1577
		DEBUGNumber(DBG_DATX, yc++, memory_get_ram_bank(), 2, col_data);
1578
		DEBUGNumber(DBG_DATX, yc++, memory_get_rom_bank(), 2, col_data);
1579
		yc++;
1580
	} else {
1581
		while (labels_c02[n] != NULL) {									// Labels
1582
			DEBUGString(dbgRenderer, DBG_LBLX,n,labels_c02[n], col_label);n++;
1583
		}
1584
		yc++;
1585
		DEBUGNumber(DBG_LBLX, yc, (regs.status >> 7) & 1, 1, col_data);
1586
		DEBUGNumber(DBG_LBLX+1, yc, (regs.status >> 6) & 1, 1, col_data);
1587
		DEBUGNumber(DBG_LBLX+3, yc, (regs.status >> 4) & 1, 1, col_data);
1588
		DEBUGNumber(DBG_LBLX+4, yc, (regs.status >> 3) & 1, 1, col_data);
1589
		DEBUGNumber(DBG_LBLX+5, yc, (regs.status >> 2) & 1, 1, col_data);
1590
		DEBUGNumber(DBG_LBLX+6, yc, (regs.status >> 1) & 1, 1, col_data);
1591
		DEBUGNumber(DBG_LBLX+7, yc, (regs.status >> 0) & 1, 1, col_data);
1592
		yc+= 2;
1593
1594
		DEBUGNumber(DBG_DATX, yc++, regs.a, 2, col_data);
1595
		DEBUGNumber(DBG_DATX, yc++, regs.xl, 2, col_data);
1596
		DEBUGNumber(DBG_DATX, yc++, regs.yl, 2, col_data);
1597
		yc++;
1598
1599
		DEBUGNumber(DBG_DATX, yc++, regs.pc, 4, col_data);
1600
		DEBUGNumber(DBG_DATX, yc++, regs.sp|0x100, 4, col_data);
1601
		DEBUGNumber(DBG_DATX, yc++, memory_get_ram_bank(), 2, col_data);
1602
		DEBUGNumber(DBG_DATX, yc++, memory_get_rom_bank(), 2, col_data);
1603
		yc++;
1604
1605
	}
1606
1607
	// The panel has room for one breakpoint but there can now be several, so
1608
	// show the first and how many more there are rather than pretending the
1609
	// others do not exist.
1610
	if (numBreakpoints == 0) {
1611
		DEBUGString(dbgRenderer, DBG_DATX, yc++, "--:----", col_data);
1612
	} else {
1613
		const struct breakpoint *bp = &breakPoints[0];
1614
		if (bp->x16Bank < 0) {
1615
			if (is_gen2) {
1616
				DEBUGNumber(DBG_DATX, yc, bp->bank, 2, col_data);
1617
				DEBUGNumber(DBG_DATX+3, yc, bp->pc, 4, col_data);
1618
			} else {
1619
				DEBUGString(dbgRenderer, DBG_DATX, yc, "--:", col_data);
1620
				DEBUGNumber(DBG_DATX+3, yc, (uint16_t)bp->pc, 4, col_data);
1621
			}
1622
		} else {
1623
			DEBUGNumber(DBG_DATX, yc, bp->x16Bank, 2, col_data);
1624
			DEBUGString(dbgRenderer, DBG_DATX+2, yc, ":", col_data);
1625
			DEBUGNumber(DBG_DATX+3, yc, bp->pc, 4, col_data);
1626
		}
1627
		if (numBreakpoints > 1) {
1628
			char more[16];
1629
			snprintf(more, sizeof(more), "+%d", numBreakpoints - 1);
1630
			DEBUGString(dbgRenderer, DBG_DATX+8, yc, more, col_data);
1631
		}
1632
		yc++;
1633
	}
1634
	yc++;
1635
1636
	return n; 													// Number of code display lines
1637
}
1638
1639
1640
static char *vera_labels[] = { "ADDR0", "ADDR1", "DATA0","DATA1", "CTRL", "VIDEO", "HSCLE", "VSCLE", "FXCTL", "FXMUL", "CACHE", "ACCUM", "", "CLOCKS ELAPSED", NULL };
1641
1642
static void DEBUGRenderVERAState(int y) {
1643
	int n=0;
1644
	int yc=y;
1645
	while (vera_labels[n] != NULL) { // Labels
1646
		DEBUGString(dbgRenderer, DBG_VERA_REGX, yc, vera_labels[n], col_label);n++;yc++;
1647
	}
1648
1649
	yc=y;
1650
1651
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_address(0), 5, col_data);
1652
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_address(1), 5, col_data);
1653
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_read(3, true), 2, col_data);
1654
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_read(4, true), 2, col_data);
1655
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_read(5, true), 2, col_data);
1656
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(0), 2, col_data);
1657
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(1), 2, col_data);
1658
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(2), 2, col_data);
1659
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(8), 2, col_data);
1660
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_dc_value(11), 2, col_data);
1661
	DEBUGNumber(DBG_VERA_REGX+6, yc, video_get_dc_value(24), 2, col_data);
1662
	DEBUGNumber(DBG_VERA_REGX+8, yc, video_get_dc_value(25), 2, col_data);
1663
	DEBUGNumber(DBG_VERA_REGX+10, yc, video_get_dc_value(26), 2, col_data);
1664
	DEBUGNumber(DBG_VERA_REGX+12, yc++, video_get_dc_value(27), 2, col_data);
1665
	DEBUGNumber(DBG_VERA_REGX+6, yc++, video_get_fx_accum(), 8, col_data);
1666
1667
	yc+=2;
1668
	DEBUGNumberDec(DBG_VERA_REGX, yc++, clockticks6502 - debugCPUClocks, 14, col_data);
1669
}
1670
1671
// *******************************************************************************************
1672
//
1673
//									Render Top of Stack
1674
//
1675
// *******************************************************************************************
1676
1677
static void DEBUGRenderStack(int bytesCount) {
1678
	uint16_t sp = regs.sp;
1679
	increment_wrap_at_page_boundary(&sp);
1680
1681
	int y= 0;
1682
	while (y < bytesCount) {
1683
		DEBUGNumber(DBG_STCK,y, sp,4, col_label);
1684
		int byte = debug_read6502(sp, 0, USE_CURRENT_X16_BANK);
1685
		DEBUGNumber(DBG_STCK+5,y,byte,2, col_data);
1686
		DEBUGWrite(dbgRenderer, DBG_STCK+9,y,byte, col_data);
1687
		y++;
1688
		increment_wrap_at_page_boundary(&sp);
1689
	}
1690
}
1691
1692
// *******************************************************************************************
1693
//
1694
//									Write Hex Constant
1695
//
1696
// *******************************************************************************************
1697
1698
static void DEBUGNumber(int x, int y, int n, int w, SDL_Color colour) {
1699
	char fmtString[8],buffer[16];
1700
	snprintf(fmtString, sizeof(fmtString), "%%0%dX", w);
1701
	snprintf(buffer, sizeof(buffer), fmtString, n);
1702
	DEBUGString(dbgRenderer, x, y, buffer, colour);
1703
}
1704
1705
// *******************************************************************************************
1706
//
1707
//					Write Decimal Constant with thousands separator
1708
//
1709
// *******************************************************************************************
1710
1711
static void DEBUGNumberDec(int x, int y, int n, int w, SDL_Color colour) {
1712
	char buf1[32], buf2[32];
1713
	int i,j;
1714
	snprintf(buf1, sizeof(buf1), "%d", n);
1715
	buf2[sizeof(buf2)-1] = 0; // null terminate string
1716
	int count = 0;
1717
	for (i=strlen(buf1) - 1, j=sizeof(buf2) - 1; i >= 0 && j > 1; i--) {
1718
		buf2[--j] = buf1[i];
1719
		count++;
1720
1721
		if (count == 3) {
1722
			buf2[--j] = ' ';
1723
			count = 0;
1724
		}
1725
	}
1726
1727
	if (buf2[j] == ' ') j++;
1728
	DEBUGString(dbgRenderer, x+(w-strlen(buf2+j)), y, buf2+j, colour);
1729
}
1730
1731
// *******************************************************************************************
1732
//
1733
//									Write Bank:Address
1734
//
1735
// *******************************************************************************************
1736
static void DEBUGAddress(int x, int y, int x16Bank, int addr, uint8_t bank, SDL_Color colour) {
1737
	char buffer[4];
1738
1739
	// Whether a window bank means anything here is the shared rule's decision,
1740
	// not a fourth local copy of it. The one that used to be here required the
1741
	// program bank to be zero, so on gen1 with -c816 it rendered "--:" at an
1742
	// address the window registers really do select.
1743
	if (debug_current_x16_bank(addr, bank) >= 0) {
1744
		snprintf(buffer, sizeof(buffer), "%.2X:", x16Bank);
1745
	} else if (is_gen2) {
1746
		snprintf(buffer, sizeof(buffer), "%.2X ", bank);
1747
	} else {
1748
		strcpy(buffer, "--:");
1749
	}
1750
1751
	DEBUGString(dbgRenderer, x, y, buffer, colour);
1752
1753
	DEBUGNumber(x+3, y, addr, 4, colour);
1754
1755
}
1756
1757
static void
1758
DEBUGVAddress(int x, int y, int addr, SDL_Color colour)
1759
{
1760
	DEBUGNumber(x, y, addr, 5, colour);
1761
}