aboutsummaryrefslogtreecommitdiff
path: root/AT91SAM7S256/armdebug/Debugger/debug_comm.S
blob: 872ddc6beefb8cc7be7ba8c97f282f9a051b098b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
/** @file debug_comm.S
 *  @brief GDB Server communications support routines
 *
 */

/* Copyright (C) 2007-2010 the NxOS developers
 *
 * Module Developed by: TC Wan <tcwan@cs.usm.my>
 *
 * See AUTHORS for a full list of the developers.
 *
 * See COPYING for redistribution license
 *
 */


#define __NXOS__			/* Temporarily hardcoded in file */

#define __ASSEMBLY__
#include "debug_stub.h"

#ifdef __NXOS__
#include "usb.h"
#else
#include "c_comm.h"
#endif

.bss
.align 4

debug_InUSBBuf:
	.space	USB_BUFSIZE,0
debug_OutUSBBuf:
	.space	USB_BUFSIZE,0

debug_msgRxBufPtr:
	.word	0x0
debug_msgTxBufPtr:
	.word	0x0

debug_segmentRxNum:				/* Current Rx Segment Number */
	.word	0x0
debug_segmentTxNum:				/* Current Tx Segment Number */
	.word	0x0

.data
.align 4

hex2char_lut:
	.ascii	"0123456789ABCDEF"

/* Macros
 */

/* _asciiz
 *		Terminate string given string buffer pointer in \addrptr
 *		reg is used as a scratch register (destroyed)
 *
 */
	.macro	_asciiz	reg, strptr
	mov		\reg, #0					/* NULL character */
	strb		\reg, [\strptr]				/* Terminate ASCIIZ string */
 	.endm

/* _hex2char_lut
 *		Internal routine to intialize the LUT address pointer
 */
	.macro _hex2char_lut	addrptr
	ldr		\addrptr, =hex2char_lut
	.endm

/* _hex2char_cont
 *		Internal routine that assumes that the LUT has been loaded.
 *		This macro accepts a byte sized hex value as a parameter register(7:0) and returns the
 *		ASCII equivalent in in the same register(7:0)
 *		The second parameter is the LUT address pointer register to use (assumed to be initialized)
 *		WARNING: Assumes that the value in register is sanity checked before invoking macro
 */
	.macro	_hex2char_cont	reg, addrptr
	ldrb	\reg, [\addrptr, \reg]
	.endm

/* _hex2char
 *		This macro accepts a byte sized hex value as a parameter register(7:0) and returns the
 *		ASCII equivalent in in the same register(7:0)
 *		The second parameter is the LUT address pointer register to use (register content is destroyed)
 *		WARNING: Assumes that the value in register is sanity checked before invoking macro
 */
	.macro	_hex2char	reg, addrptr
	_hex2char_lut	\addrptr
	_hex2char_cont	\reg, \addrptr
	.endm

/* _char2hex
 *		This macro accepts an ASCII char as a parameter register(7:0) and returns the
 *		equivalent byte sized hex value in in the same register(7:0)
 *		WARNING: Assumes that the char in register is a valid hex char before invoking macro
 */
	.macro	_char2hex	reg
	cmp	\reg, #'A'									/* If Alpha */
	bichs	\reg, \reg, #ASCII_LOWER2UPPER_MASK		/* Convert to Uppercase */
	subhs	\reg, \reg, #7							/* Adjustment to allow for subtraction with 0x30 */
	sub		\reg, \reg, #0x30						/* get final hex value */
	.endm


.code 32
.text
.align 	4


/* Utility Routines
 */

/* hex2char
 *		This routine accepts a byte sized hex value in R0(7:0) and returns the
 *		ASCII equivalent in R0(7:0)
 */
	.global	hex2char

hex2char:
	stmfd	sp!, {r1,lr}
	and		r0, #NIBBLE0			/* make sure that input is sane */
	_hex2char	r0, r1
	ldmfd	sp!, {r1,pc}

/* char2hex
 *		This routine accepts an ASCII character in R0(7:0) and returns the
 *		equivalent byte sized hex value in R0(7:0)
 */
	.global	char2hex

char2hex:
	and		r0, #BYTE0			/* make sure that input is sane */
	cmp		r0, #'0'
	blo		exit_char2hex
	cmp		r0, #'F'
	bhi		exit_char2hex
	_char2hex	r0
exit_char2hex:
	bx		lr

/* byte2ascii_cont
 *		This routine accepts a byte value in R0(7:0), and a ASCII buffer pointer in R1,
 *		and stores the ASCII equivalent byte value in the buffer pointed to by R1.
 *		Note: On return, R1 points to next empty char slot in buffer (i.e., R1 is modified)
 *			  and R0 is destroyed.
 */
byte2ascii_cont:
	stmfd	sp!, {r2,r3,r4, lr}
	mov		r2, r0, lsl #24			/* Keep copy of input byte value R0(7:0), shifted to MSB R2(31:24) */
	mov		r4, #2					/* Loop counter */
	_hex2char_lut	r3				/* initialize LUT pointer */
1:	mov		r0, r2, ror #28			/* Rotate MSNibble R2(31:28) into LSNibble position R0(3:0) */
	and		r0, r0, #NIBBLE0		/* Mask out everything else */
	_hex2char_cont	r0, r3			/* Convert nibble to ASCII char */
	strb	r0, [r1], #1
	subs	r4, r4, #1				/* decrement loop counter */
	bne		1b
	ldmfd	sp!, {r2,r3,r4, pc}

/* byte2ascii
 *		This routine accepts a byte value in R0(7:0), and a ASCII buffer pointer in R1,
 *		and stores the ASCII equivalent byte value in the buffer pointed to by R1.
 *		Note: On return, R1 points to the end of the ASCIIZ string (i.e. NULL character)
 */
	.global	byte2ascii

byte2ascii:
	stmfd	sp!, {r1, lr}			/* Keep ASCII buffer pointer */
	and		r0, #BYTE0				/* sanitize input */
	bl		byte2ascii_cont
	_asciiz	r0, r1
	ldmfd	sp!, {r0, pc}			/* return string pointer in R0 */

/* halfword2ascii
 *		This routine accepts a halfword value in R0(15:0), and a ASCII buffer pointer in R1,
 *		and returns the ASCIIZ equivalent byte value in the buffer pointed to by R0.
 *		Note: On return, R1 points to the end of the ASCIIZ string (i.e. NULL character)
 */
	.global	halfword2ascii
halfword2ascii:
	stmfd	sp!, {r1,r2,r3, lr}		/* Keep ASCII buffer pointer */
	mov		r2, r0, lsl #16			/* copy of input halfword value R0(15:0), shifted to MSH R2(31:16) */
	mov		r3, #2					/* Loop Counter */
	b		_conv_byte2ascii		/* goto Byte conversion loop */

/* word2ascii
 *		This routine accepts a word value in R0(31:0), and a ASCII buffer pointer in R1,
 *		and returns the ASCIIZ equivalent byte value in the buffer pointed to by R0.
 *		Note: On return, R1 points to the end of the ASCIIZ string (i.e. NULL character)
 */
	.global	word2ascii
word2ascii:
	stmfd	sp!, {r1,r2,r3, lr}		/* Keep ASCII buffer pointer */
	mov		r2, r0					/* copy of input word value R0(31:0) */
	mov		r3, #4					/* Loop Counter */

	/* Fall through to byte coversion loop */

_conv_byte2ascii:
	mov		r0, r2, ror #24			/* Rotate MSB R2(31:24) into LSB position R0(7:0) */
	and		r0, #BYTE0			/* Mask out everything else */
	bl		byte2ascii_cont
	subs	r3, r3, #1
	bne		_conv_byte2ascii
	_asciiz	r0, r1
	ldmfd	sp!, {r0,r2,r3, pc}


/* ascii2byte
 *		This routine accepts an ASCII buffer pointer in R0,
 *		and returns the byte value in R0(7:0).
 *		Note: On return, R1 points to the ASCII buffer location after the current 2 chars.
 *		WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars,
 *				 otherwise it will return invalid results.
 */
	.global	ascii2byte

ascii2byte:
	stmfd	sp!, {r2,r3, lr}
	mov		r3, #2					/* Loop counter */
	b		_conv_ascii2byte

/* ascii2halfword
 *		This routine accepts an ASCII buffer pointer in R0,
 *		and returns the word value in R0(15:0).
 *		Note: On return, R1 points to the ASCII buffer location after the current 4 chars.
 *		WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars,
 *				 otherwise it will return invalid results.
 */
	.global	ascii2halfword

ascii2halfword:
	stmfd	sp!, {r2,r3, lr}
	mov		r3, #4					/* Loop counter */
	b		_conv_ascii2byte


/* ascii2word
 *		This routine accepts an ASCII buffer pointer in R0,
 *		and returns the word value in R0(31:0).
 *		Note: On return, R1 points to the ASCII buffer location after the current 8 chars.
 *		WARNING: This routine assumes that the input buffer was sanitized and contains valid Hex chars,
 *				 otherwise it will return invalid results.
 */
	.global	ascii2word

ascii2word:
	stmfd	sp!, {r2,r3, lr}
	mov		r3, #8					/* Loop counter */

	/* Fall through to byte coversion loop */

_conv_ascii2byte:
	teq		r0, #0
	beq		_exit_conv_ascii2byte	/* exit if NULL pointer in R0 */
	mov		r0, r1					/* Copy of ASCII buffer pointer */
	mov		r2, #0					/* Initialize results */
2:	ldrb	r0, [r1], #1			/* Load ASCII char */
	bl		char2hex				/* on return, hex value in R0 */
	orr		r2, r0, r2, lsl #4		/* merge Nibble into results */
	subs	r3, r3, #1
	bne		2b
	mov		r0, r2					/* Copy it to R0 as return value */
_exit_conv_ascii2byte:
	ldmfd	sp!, {r2,r3, pc}		/* return hex value in R0 */




/* Debugger Communications Routines
 *		It does not make sense to pass information from the Debugger Module to the Comm. link one character
 *		at a time, especially if we're not using a native serial interface (e.g., EIA-232). Consequently
 *		a Message interface has been defined. This can still call getChar() and putChar() subroutines
 *		if so desired, but it'll be a purely internal matter.
 *
 * Message Format
 *		Since we need to use EP1&2 (Bulk channels) to communicate with the PC Host, the messages should
 *		follow the NXT Direct Commands message structure (this will allow for interoperability with NXT Firmware
 *		in addition to NxOS). The maximum length of any USB communications via the Bulk channel is 64 bytes.
 *		There is a one byte Telegram Type field which identifies the type of telegram, followed by the
 *		Telegram header and actual message.
 *
 *		[The GDB Message Format is derived from the MESSAGEWRITE Direct Command format]
 *		The LEGO Mindstorms Communications Protocol Direct Commands GDB Message format (including all headers)
 *		is as follows:
 *
 *		GDB Command
 *		===========
 *			Byte 0:	Telegram Type Field (0x00 Direct Command, Response required)	| USB Channel Header
 *			Byte 1: Command Field (0xF0: GDB command)								| NXT Command Header
 *			Byte 2:	Segment No (1-255, 0: Last Segment; limit is MSG_NUMSEGMENTS)	|
 *			Byte 3:	Telegram Size (Len of USB Buffer - 4, max is MSG_SEGMENTSIZE)	|
 *			Byte 4-N: Message data													| GDB Command
 *
 *		The GDB Command (of size M) has the following format:
 *				Offset 0: '$'
 *				Offset 1: GDB Command char
 *				Offset 2 - (M-4): Command packet info
 *				Offset M-3: '#'
 *				Offset M-2: MSB of Checksum
 *				Offset M-1: LSB of Checksum
 *
 *		The maximum size of a GDB Command packet is MSGBUF_SIZE - 5 ('$', '#', 2 byte checksum, trailing NULL char)
 *
 *		GDB Response
 *		============
 *			Byte 0:	Telegram Type Field (0x02 Response)								| USB Channel Header
 *			Byte 1: Command Field (0xF1: GDB response)								| NXT Command Header
 *			Byte 2:	Segment No (1-255, 0: Last Segment; limit is MSG_NUMSEGMENTS)	|
 *			Byte 3:	Telegram Size (Len of USB Buffer - 4, max is MSG_SEGMENTSIZE)	|
 *			Byte 4-N: Message data													| GDB Response
 *
 *		The GDB Response (of size M) has the following format:
 *				Offset 0: '+' or '-' Command Received Status
 *				Offset 1: '$'
 *				Offset 2 - (M-4): Response packet info
 *				Offset M-3: '#'
 *				Offset M-2: MSB of Checksum
 *				Offset M-1: LSB of Checksum
 *
 *		The maximum size of a GDB Response packet is MSGBUF_SIZE - 6 ('-'/'+', '$', '#', 2 byte checksum, trailing NULL char)
 *
 *		Note: The Telegram Size is the actual size of the Message Data portion
 *			  (i.e., excludes the four header bytes, includes the GDB Command/Response Packet trailing NULL character
 *					 in the last segment)
 */

 	.global dbg__comm_init
/* dbg__comm_init
 *		Initialize communications channel.
 *		On Entry:
 *			R0: MSG Rx Buf Pointer
 *			R1: MSG Tx Buf Pointer
 */

dbg__comm_init:
#ifdef __NXOS__
	stmfd	sp!, {lr}
	ldr		r2, =debug_msgRxBufPtr
	stmia	r2, {r0,r1}							/* Assume that the 2 pointers are consecutive */
	bl		_dbg__usbbuf_reset
	ldmfd	sp!, {pc}

_dbg__usbbuf_reset:
	stmfd	sp!, {lr}
	ldr		r0, =debug_InUSBBuf
	ldr		r1, #USB_BUFSIZE
	ldr		r2, =nx_usb_read
	bl		r2
	ldmfd	sp!, {pc}
#else
	/* FIXME: NXT Firmware support */
	bx		lr
#endif

	.global	dbg__hasDebugMsg
/* dbg__hasDebugMsg
 *		Checks for pending Debugger Message (Non-Blocking).
 *		On exit:
 *			r0: !0: (Availale Debugger Message Size), 0: no pending message
 *			r1: message segment number
 */
dbg__hasDebugMsg:
#ifdef __NXOS__
	stmfd	sp!, {lr}
	ldr		r0, =nx_usb_data_read
	bl		r2					/* Number of bytes read in R0 */
	/* Note: The return value is the USB Buffer Size, includes NXT Direct Command Header */
	/* FIXME: Need to check command type etc. before accepting it as a valid Debugger message */
	ldr		r2, =debug_InUSBBuf
	ldrb	r0, [r2, #USB_NXT_TELEGRAMSIZE_OFFSET]
	ldrb	r1, [r2, #USB_NXT_SEGNUM_OFFSET]
	ldmfd	sp!, {pc}
#else
	/* FIXME: NXT Firmware support */
	bx		lr
#endif

	.global	dbg__getDebugMsg
/* dbg__getDebugMsg
 *		Returns Debugger Message to calling routine after verifying and removing checksum (Blocking).
 *		On entry:
 *			r0: address of message buffer
 *			r1: maximum size of message buffer (incl NULL character)
 *		On exit:
 *			r0: address of message buffer with NULL terminated message, excluding '#<checksum>'
 *				(NULL if message error)
 *
 */
 /* FIXME: This does not handle multiple segments currently, we just assume that everything is in one segment */
dbg__getDebugMsg:
#ifdef __NXOS__
	stmfd	sp!, {lr}
	bl		dbg__hasDebugMsg
	cmp		r0, #0
	ble		_exit_getDebugMsg		/* Zero length message, skip */
	teq		r1, #0
	bne		_exit_getDebugMsg		/* Don't process segmented messages for now */

_copy_msg_from_usbbuf:
	/* FIXME: handle multiple segments */
	ldr		r1, =debug_InUSBBuf
	ldrb	r2, [r1, #USB_GDBMSG_START]
	mov		r3, #MSGBUF_STARTCHAR
	teq		r2, r3
	bne		_exit_getDebugMsg		/* Debugger Message does not have valid start char '$' */
	ldrb	r2, [r1, r0]
	cmp		r2, #0					/* Check for NULL (in last segment) */
	bne		_exit_getDebugMsg		/* Debugger Message does not have valid terminating NULL char */
	sub		r3, r0, #USB_GDBMSG_CHKSUMOFFSET	/* Message Length - 4 = '#' offset */
	ldrb	r2, [r1, r3]
	mov		r3, #MSGBUF_CHKSUMCHAR
	teq		r2, r3
	bne		_exit_getDebugMsg		/* Debugger Message does not have valid checksum char */

	/* FIXME: Perform Checksum verification */

	mov		r3, r0				/* Setup size param for memcpy macro */
	ldr		r2, =debug_msgRxBufPtr
	ldr		r2, [r2]
	add		r1, r1, #USB_GDBMSG_START
	_dbg_memcpy	r2, r1, r3
	/* FIXME: Need to check command type etc. before accepting it as a valid Debugger message */
_exit_getDebugMsg:
	bl		_dbg__usbbuf_reset	/* Next USB telegram transaction */
	ldmfd	sp!, {pc}
#else
	/* FIXME: NXT Firmware support */
	bx		lr
#endif

	.global dbg__putDebugMsg
/* dbg__putDebugMsg
 *		Sends Debugger Message from calling routine after appending checksum (Blocking) .
 *		On entry:
 *			r0: address of message buffer with NULL terminated message, without '#<checksum>'
 *		On exit:
 *			r0: status (0: success, -1: error)
 */
dbg__putDebugMsg:
	bx		lr


/* Private functions (if needed) */
_dbg__getChar:
_dbg__putChar:
	bx		lr