Skip to content

Commit d10f7f5

Browse files
authored
Debugger memory views (#133)
* Read memory view * Add string format for watches * Add numerical address option for watches
1 parent 0a1eb57 commit d10f7f5

File tree

1 file changed

+77
-9
lines changed

1 file changed

+77
-9
lines changed

src/client/debugger/debugger.ts

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
StackFrame,
2323
StoppedEvent,
2424
TerminatedEvent,
25+
MemoryEvent,
2526
Thread,
2627
Variable,
2728
} from '@vscode/debugadapter'
@@ -134,6 +135,7 @@ const enum displayFormat {
134135
hex = 'hex',
135136
binary = 'binary',
136137
decimal = 'decimal',
138+
string = 'string',
137139
}
138140

139141
// export class JSBeebDebugSession implements DebugAdapter {
@@ -271,6 +273,8 @@ export class JSBeebDebugSession extends LoggingDebugSession {
271273

272274
response.body.supportsSteppingGranularity = false
273275

276+
response.body.supportsReadMemoryRequest = true
277+
274278
this.sendResponse(response)
275279

276280
// since this debug adapter can accept configuration requests like 'setBreakpoint' at any time,
@@ -605,7 +609,7 @@ export class JSBeebDebugSession extends LoggingDebugSession {
605609
// eslint-disable-next-line @typescript-eslint/no-unused-vars
606610
request?: DebugProtocol.Request | undefined,
607611
): Promise<void> {
608-
console.log(args)
612+
// console.log(args)
609613
console.log(`${new Date().toLocaleTimeString()} variablesRequest called`)
610614
const variables: Variable[] = []
611615
if (args.filter === undefined || args.filter === 'named') {
@@ -624,6 +628,12 @@ export class JSBeebDebugSession extends LoggingDebugSession {
624628
['Cycles', 'Next op'].includes(reg.name),
625629
)
626630
variables.push(...system)
631+
const memory: DebugProtocol.Variable = new Variable(
632+
'Memory',
633+
'Click icon to view',
634+
)
635+
memory.memoryReference = 'memory'
636+
variables.push(memory)
627637
}
628638
}
629639
response.body = { variables: variables }
@@ -685,6 +695,46 @@ export class JSBeebDebugSession extends LoggingDebugSession {
685695
return vars
686696
}
687697

698+
protected async readMemoryRequest(
699+
response: DebugProtocol.ReadMemoryResponse,
700+
{ memoryReference, offset = 0, count }: DebugProtocol.ReadMemoryArguments,
701+
): Promise<void> {
702+
console.log(`${new Date().toLocaleTimeString()} readMemoryRequest called`)
703+
// console.log({ memoryReference, offset, count })
704+
if (memoryReference === 'memory' && count !== 0) {
705+
if (offset > this.memory.length) {
706+
response.body = {
707+
address: offset.toString(),
708+
unreadableBytes: count,
709+
data: '',
710+
}
711+
} else {
712+
const unreadableBytes = Math.max(offset + count - this.memory.length, 0)
713+
response.body = {
714+
address: offset.toString(),
715+
unreadableBytes: unreadableBytes,
716+
data: this.convertMemoryToBase64(offset, count - unreadableBytes),
717+
}
718+
}
719+
} else {
720+
response.body = {
721+
address: offset.toString(),
722+
unreadableBytes: 0,
723+
data: '',
724+
}
725+
}
726+
this.sendResponse(response)
727+
}
728+
729+
private convertMemoryToBase64(offset: number, count: number): string {
730+
if (count == 0) {
731+
count = this.memory.length - offset
732+
}
733+
return Buffer.from(this.memory.slice(offset, offset + count)).toString(
734+
'base64',
735+
)
736+
}
737+
688738
protected async stackTraceRequest(
689739
response: DebugProtocol.StackTraceResponse,
690740
args: DebugProtocol.StackTraceArguments,
@@ -727,6 +777,8 @@ export class JSBeebDebugSession extends LoggingDebugSession {
727777
current = current.parent
728778
}
729779
this.sendResponse(response)
780+
// Since we refreshed the memory, emit a MemoryEvent
781+
this.sendEvent(new MemoryEvent('memory', 0, this.memory.length))
730782
}
731783
// console.log(response)
732784
}
@@ -737,8 +789,11 @@ export class JSBeebDebugSession extends LoggingDebugSession {
737789
// eslint-disable-next-line @typescript-eslint/no-unused-vars
738790
request?: DebugProtocol.Request,
739791
): Promise<void> {
740-
console.log(args)
741-
const validExp = /([$&%.]|\.\*|\.\^)?([a-z][a-z0-9_]*)(\.[wd])?/gi
792+
console.log(
793+
`${new Date().toLocaleTimeString()} evaluateRequest '${args.expression}'`,
794+
)
795+
const validExp =
796+
/([$&%.]|\.\*|\.\^)?([a-z][a-z0-9_]*|\([$&][0-9a-f]+\)|\([0-9]+\))(\.[wd]|\.s[0-9]+)?/gi
742797
if (args.context === 'watch') {
743798
const match = validExp.exec(args.expression)
744799
if (match === null) {
@@ -747,7 +802,7 @@ export class JSBeebDebugSession extends LoggingDebugSession {
747802
}
748803
const formatPrefix = match[1] ?? ''
749804
let label = match[2]
750-
const sizeSuffix = match[3] ?? 'b'
805+
const sizeSuffix = (match[3] ?? '.b').toLowerCase()
751806
let format: displayFormat
752807
switch (formatPrefix) {
753808
case '&':
@@ -774,12 +829,14 @@ export class JSBeebDebugSession extends LoggingDebugSession {
774829
case '.d':
775830
bytes = 4
776831
break
777-
default:
832+
case '.b':
778833
bytes = 1
779834
break
835+
default:
836+
format = displayFormat.string
837+
bytes = parseInt(sizeSuffix.slice(2), 10)
838+
break
780839
}
781-
// TODO - consider adding ".s" suffix for strings
782-
// TODO - maybe signed/unsigned variants? Big/little endian?
783840

784841
// search for label in labels list after removing prefix if necessary
785842
if (label.startsWith('.')) {
@@ -790,7 +847,8 @@ export class JSBeebDebugSession extends LoggingDebugSession {
790847
}
791848
if (
792849
this.labelMap['.' + label] === undefined &&
793-
this.symbolMap[label] === undefined
850+
this.symbolMap[label] === undefined &&
851+
!label.startsWith('(')
794852
) {
795853
this.sendErrorResponse(response, 0, '')
796854
return
@@ -805,7 +863,13 @@ export class JSBeebDebugSession extends LoggingDebugSession {
805863
}
806864

807865
let address: number
808-
if (this.labelMap['.' + label] !== undefined) {
866+
if (label.startsWith('(')) {
867+
if (label.charAt(1) === '&' || label.charAt(1) === '$') {
868+
address = parseInt(label.slice(2, -1), 16)
869+
} else {
870+
address = parseInt(label.slice(1, -1), 10)
871+
}
872+
} else if (this.labelMap['.' + label] !== undefined) {
809873
address = this.labelMap['.' + label]
810874
} else {
811875
address = this.symbolMap[label]
@@ -828,6 +892,10 @@ export class JSBeebDebugSession extends LoggingDebugSession {
828892
.padStart(2 * bytes, '0')}`
829893
} else if (format === displayFormat.binary) {
830894
displayValue = `%${value.toString(2).padStart(8 * bytes, '0')}`
895+
} else if (format === displayFormat.string) {
896+
displayValue = String.fromCharCode(
897+
...this.memory.slice(address, address + bytes),
898+
)
831899
} else {
832900
displayValue = `${value}`
833901
}

0 commit comments

Comments
 (0)