1 /* gdbserve.c -- NLM debugging stub for Novell NetWare.
3 This is originally based on an m68k software stub written by Glenn
4 Engel at HP, but has changed quite a bit. It was modified for the
5 i386 by Jim Kingdon, Cygnus Support. It was modified to run under
6 NetWare by Ian Lance Taylor, Cygnus Support.
8 This code is intended to produce an NLM (a NetWare Loadable Module)
9 to run under Novell NetWare. To create the NLM, compile this code
10 into an object file using the NLM SDK on any i386 host, and use the
11 nlmconv program (available in the GNU binutils) to transform the
12 resulting object file into an NLM. */
14 /****************************************************************************
16 THIS SOFTWARE IS NOT COPYRIGHTED
18 HP offers the following for use in the public domain. HP makes no
19 warranty with regard to the software or it's performance and the
20 user accepts the software "AS IS" with all faults.
22 HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
23 TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26 ****************************************************************************/
28 /****************************************************************************
30 * The following gdb commands are supported:
32 * command function Return value
34 * g return the value of the CPU registers hex data or ENN
35 * G set the value of the CPU registers OK or ENN
37 * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
38 * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
40 * c Resume at current address SNN ( signal NN)
41 * cAA..AA Continue at address AA..AA SNN
43 * s Step one instruction SNN
44 * sAA..AA Step one instruction from AA..AA SNN
48 * ? What was the last sigval ? SNN (signal NN)
50 * All commands and responses are sent with a packet which includes a
51 * checksum. A packet consists of
53 * $<packet info>#<checksum>.
56 * <packet info> :: <characters representing the command or response>
57 * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
59 * When a packet is received, it is first acknowledged with either '+' or '-'.
60 * '+' indicates a successful transfer. '-' indicates a failed transfer.
65 * $m0,10#2a +$00010203040506070809101112131415#42
67 ****************************************************************************/
95 /****************************************************/
96 /* This information is from Novell. It is not in any of the standard
97 NetWare header files. */
99 struct DBG_LoadDefinitionStructure
103 LONG LDCodeImageOffset
;
104 LONG LDCodeImageLength
;
105 LONG LDDataImageOffset
;
106 LONG LDDataImageLength
;
107 LONG LDUninitializedDataLength
;
108 LONG LDCustomDataOffset
;
109 LONG LDCustomDataSize
;
111 LONG (*LDInitializationProcedure
)(void);
114 #define LO_NORMAL 0x0000
115 #define LO_STARTUP 0x0001
116 #define LO_PROTECT 0x0002
117 #define LO_DEBUG 0x0004
118 #define LO_AUTO_LOAD 0x0008
120 /* Loader returned error codes */
121 #define LOAD_COULD_NOT_FIND_FILE 1
122 #define LOAD_ERROR_READING_FILE 2
123 #define LOAD_NOT_NLM_FILE_FORMAT 3
124 #define LOAD_WRONG_NLM_FILE_VERSION 4
125 #define LOAD_REENTRANT_INITIALIZE_FAILURE 5
126 #define LOAD_CAN_NOT_LOAD_MULTIPLE_COPIES 6
127 #define LOAD_ALREADY_IN_PROGRESS 7
128 #define LOAD_NOT_ENOUGH_MEMORY 8
129 #define LOAD_INITIALIZE_FAILURE 9
130 #define LOAD_INCONSISTENT_FILE_FORMAT 10
131 #define LOAD_CAN_NOT_LOAD_AT_STARTUP 11
132 #define LOAD_AUTO_LOAD_MODULES_NOT_LOADED 12
133 #define LOAD_UNRESOLVED_EXTERNAL 13
134 #define LOAD_PUBLIC_ALREADY_DEFINED 14
135 /****************************************************/
137 /* The main thread ID. */
138 static int mainthread
;
140 /* An error message for the main thread to print. */
141 static char *error_message
;
143 /* The AIO port handle. */
144 static int AIOhandle
;
146 /* BUFMAX defines the maximum number of characters in inbound/outbound
147 buffers. At least NUMREGBYTES*2 are needed for register packets */
148 #define BUFMAX (REGISTER_BYTES * 2 + 16)
150 /* remote_debug > 0 prints ill-formed commands in valid packets and
152 static int remote_debug
= 1;
154 static const char hexchars
[] = "0123456789abcdef";
156 unsigned char breakpoint_insn
[] = BREAKPOINT
;
158 char *mem2hex (void *mem
, char *buf
, int count
, int may_fault
);
159 char *hex2mem (char *buf
, void *mem
, int count
, int may_fault
);
160 extern void set_step_traps (struct StackFrame
*);
161 extern void clear_step_traps (struct StackFrame
*);
163 static int __main() {};
165 /* Read a character from the serial port. This must busy wait, but
166 that's OK because we will be the only thread running anyhow. */
177 err
= AIOReadData (AIOhandle
, (char *) &ret
, 1, &got
);
180 error_message
= "AIOReadData failed";
181 ResumeThread (mainthread
);
190 /* Write a character to the serial port. Returns 0 on failure,
191 non-zero on success. */
203 err
= AIOWriteData (AIOhandle
, (char *) &c
, 1, &put
);
205 ConsolePrintf ("AIOWriteData: err = %d, put = %d\r\n", err
, put
);
210 /* Turn a hex character into a number. */
216 if ((ch
>= 'a') && (ch
<= 'f'))
218 if ((ch
>= '0') && (ch
<= '9'))
220 if ((ch
>= 'A') && (ch
<= 'F'))
225 /* Scan for the sequence $<data>#<checksum>. Returns 0 on failure,
226 non-zero on success. */
232 unsigned char checksum
;
233 unsigned char xmitcsum
;
240 /* wait around for the start character, ignore all other characters */
241 while ((ch
= getDebugChar()) != '$')
249 /* now, read until a # or end of buffer is found */
250 while (count
< BUFMAX
)
257 checksum
= checksum
+ ch
;
265 ch
= getDebugChar ();
268 xmitcsum
= hex(ch
) << 4;
269 ch
= getDebugChar ();
274 if (checksum
!= xmitcsum
)
277 ConsolePrintf ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n",
278 checksum
,xmitcsum
,buffer
);
279 /* failed checksum */
280 if (! putDebugChar('-'))
286 /* successful transfer */
287 if (! putDebugChar('+'))
289 /* if a sequence char is present, reply the sequence ID */
290 if (buffer
[2] == ':')
292 if (! putDebugChar (buffer
[0])
293 || ! putDebugChar (buffer
[1]))
295 /* remove sequence chars from buffer */
296 count
= strlen(buffer
);
297 for (i
=3; i
<= count
; i
++)
298 buffer
[i
-3] = buffer
[i
];
303 while (checksum
!= xmitcsum
);
306 ConsolePrintf ("Received packet \"%s\"\r\n", buffer
);
311 /* Send the packet in buffer. Returns 0 on failure, non-zero on
318 unsigned char checksum
;
323 ConsolePrintf ("Sending packet \"%s\"\r\n", buffer
);
325 /* $<packet info>#<checksum>. */
328 if (! putDebugChar('$'))
333 while (ch
=buffer
[count
])
335 if (! putDebugChar(ch
))
341 if (! putDebugChar('#')
342 || ! putDebugChar(hexchars
[checksum
>> 4])
343 || ! putDebugChar(hexchars
[checksum
% 16]))
346 ch
= getDebugChar ();
355 static char remcomInBuffer
[BUFMAX
];
356 static char remcomOutBuffer
[BUFMAX
];
360 debug_error (format
, parm
)
366 ConsolePrintf (format
, parm
);
367 ConsolePrintf ("\n");
371 /* This is set if we could get a memory access fault. */
372 static int mem_may_fault
;
374 /* Indicate to caller of mem2hex or hex2mem that there has been an
376 volatile int mem_err
= 0;
378 #ifndef ALTERNATE_MEM_FUNCS
379 /* These are separate functions so that they are so short and sweet
380 that the compiler won't save any registers (if there is a fault
381 to mem_fault, they won't get restored, so there better not be any
398 #endif /* ALTERNATE_MEM_FUNCS */
400 /* convert the memory pointed to by mem into hex, placing result in buf */
401 /* return a pointer to the last char put in buf (null) */
402 /* If MAY_FAULT is non-zero, then we should set mem_err in response to
403 a fault; if zero treat a fault like any other fault in the stub. */
406 mem2hex (mem
, buf
, count
, may_fault
)
416 mem_may_fault
= may_fault
;
417 for (i
= 0; i
< count
; i
++)
419 ch
= get_char (ptr
++);
420 if (may_fault
&& mem_err
)
422 *buf
++ = hexchars
[ch
>> 4];
423 *buf
++ = hexchars
[ch
% 16];
430 /* convert the hex array pointed to by buf into binary to be placed in mem */
431 /* return a pointer to the character AFTER the last byte written */
434 hex2mem (buf
, mem
, count
, may_fault
)
444 mem_may_fault
= may_fault
;
445 for (i
=0;i
<count
;i
++)
447 ch
= hex(*buf
++) << 4;
448 ch
= ch
+ hex(*buf
++);
449 set_char (ptr
++, ch
);
450 if (may_fault
&& mem_err
)
457 /* This function takes the 386 exception vector and attempts to
458 translate this number into a unix compatible signal value. */
461 computeSignal (exceptionVector
)
465 switch (exceptionVector
)
467 case 0 : sigval
= 8; break; /* divide by zero */
468 case 1 : sigval
= 5; break; /* debug exception */
469 case 3 : sigval
= 5; break; /* breakpoint */
470 case 4 : sigval
= 16; break; /* into instruction (overflow) */
471 case 5 : sigval
= 16; break; /* bound instruction */
472 case 6 : sigval
= 4; break; /* Invalid opcode */
473 case 7 : sigval
= 8; break; /* coprocessor not available */
474 case 8 : sigval
= 7; break; /* double fault */
475 case 9 : sigval
= 11; break; /* coprocessor segment overrun */
476 case 10 : sigval
= 11; break; /* Invalid TSS */
477 case 11 : sigval
= 11; break; /* Segment not present */
478 case 12 : sigval
= 11; break; /* stack exception */
479 case 13 : sigval
= 11; break; /* general protection */
480 case 14 : sigval
= 11; break; /* page fault */
481 case 16 : sigval
= 7; break; /* coprocessor error */
483 sigval
= 7; /* "software generated"*/
488 /**********************************************/
489 /* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
490 /* RETURN NUMBER OF CHARS PROCESSED */
491 /**********************************************/
493 hexToInt(ptr
, intValue
)
504 hexValue
= hex(**ptr
);
507 *intValue
= (*intValue
<<4) | hexValue
;
519 /* This function does all command processing for interfacing to gdb.
520 It is called whenever an exception occurs in the module being
524 handle_exception (frame
)
525 struct StackFrame
*frame
;
529 static struct DBG_LoadDefinitionStructure
*ldinfo
= 0;
530 static unsigned char first_insn
[BREAKPOINT_SIZE
]; /* The first instruction in the program. */
532 /* Apparently the bell can sometimes be ringing at this point, and
533 should be stopped. */
538 ConsolePrintf ("vector=%d: %s, pc=%08x, thread=%08x\r\n",
539 frame
->ExceptionNumber
,
540 frame
->ExceptionDescription
,
545 switch (frame
->ExceptionNumber
)
547 case START_NLM_EVENT
:
548 /* If the NLM just started, we record the module load information
549 and the thread ID, and set a breakpoint at the first instruction
552 ldinfo
= ((struct DBG_LoadDefinitionStructure
*)
553 frame
->ExceptionErrorCode
);
554 memcpy (first_insn
, ldinfo
->LDInitializationProcedure
,
556 memcpy (ldinfo
->LDInitializationProcedure
, breakpoint_insn
,
559 return RETURN_TO_PROGRAM
;
561 case ENTER_DEBUGGER_EVENT
:
562 case KEYBOARD_BREAK_EVENT
:
563 /* Pass some events on to the next debugger, in case it will handle
565 return RETURN_TO_NEXT_DEBUGGER
;
567 case 3: /* Breakpoint */
568 /* After we've reached the initial breakpoint, reset it. */
569 if (frame
->ExceptionPC
- DECR_PC_AFTER_BREAK
== (LONG
) ldinfo
->LDInitializationProcedure
570 && memcmp (ldinfo
->LDInitializationProcedure
, breakpoint_insn
,
571 BREAKPOINT_SIZE
) == 0)
573 memcpy (ldinfo
->LDInitializationProcedure
, first_insn
,
575 frame
->ExceptionPC
-= DECR_PC_AFTER_BREAK
;
578 /* Normal breakpoints end up here */
579 do_status (remcomOutBuffer
, frame
);
583 /* At the moment, we don't care about most of the unusual NetWare
585 if (frame
->ExceptionNumber
> 31)
586 return RETURN_TO_PROGRAM
;
588 /* Most machine level exceptions end up here */
589 do_status (remcomOutBuffer
, frame
);
592 case 11: /* Segment not present */
593 case 13: /* General protection */
594 case 14: /* Page fault */
595 /* If we get a GP fault, and mem_may_fault is set, and the
596 instruction pointer is near set_char or get_char, then we caused
597 the fault ourselves accessing an illegal memory location. */
599 && ((frame
->ExceptionPC
>= (long) &set_char
600 && frame
->ExceptionPC
< (long) &set_char
+ 50)
601 || (frame
->ExceptionPC
>= (long) &get_char
602 && frame
->ExceptionPC
< (long) &get_char
+ 50)))
605 /* Point the instruction pointer at an assembly language stub
606 which just returns from the function. */
608 frame
->ExceptionPC
+= 4; /* Skip the load or store */
610 /* Keep going. This will act as though it returned from
611 set_char or get_char. The calling routine will check
612 mem_err, and do the right thing. */
613 return RETURN_TO_PROGRAM
;
615 /* Random mem fault, report it */
616 do_status (remcomOutBuffer
, frame
);
619 case TERMINATE_NLM_EVENT
:
620 /* There is no way to get the exit status. */
621 sprintf (remcomOutBuffer
, "W%02x", 0);
622 break; /* We generate our own status */
625 /* FIXME: How do we know that this exception has anything to do with
626 the program we are debugging? We can check whether the PC is in
627 the range of the module we are debugging, but that doesn't help
628 much since an error could occur in a library routine. */
630 clear_step_traps (frame
);
632 if (! putpacket(remcomOutBuffer
))
633 return RETURN_TO_NEXT_DEBUGGER
;
635 if (frame
->ExceptionNumber
== TERMINATE_NLM_EVENT
)
637 ResumeThread (mainthread
);
638 return RETURN_TO_PROGRAM
;
644 remcomOutBuffer
[0] = 0;
645 if (! getpacket (remcomInBuffer
))
646 return RETURN_TO_NEXT_DEBUGGER
;
647 switch (remcomInBuffer
[0])
650 do_status (remcomOutBuffer
, frame
);
653 remote_debug
= !(remote_debug
); /* toggle debug flag */
656 /* return the value of the CPU registers */
657 frame_to_registers (frame
, remcomOutBuffer
);
660 /* set the value of the CPU registers - return OK */
661 registers_to_frame (&remcomInBuffer
[1], frame
);
662 strcpy(remcomOutBuffer
,"OK");
666 /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
667 /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
668 ptr
= &remcomInBuffer
[1];
669 if (hexToInt(&ptr
,&addr
))
671 if (hexToInt(&ptr
,&length
))
675 mem2hex((char*) addr
, remcomOutBuffer
, length
, 1);
678 strcpy (remcomOutBuffer
, "E03");
679 debug_error ("memory fault");
685 strcpy(remcomOutBuffer
,"E01");
686 debug_error("malformed read memory command: %s",remcomInBuffer
);
691 /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
692 /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
693 ptr
= &remcomInBuffer
[1];
694 if (hexToInt(&ptr
,&addr
))
696 if (hexToInt(&ptr
,&length
))
700 hex2mem(ptr
, (char*) addr
, length
, 1);
704 strcpy (remcomOutBuffer
, "E03");
705 debug_error ("memory fault");
709 strcpy(remcomOutBuffer
,"OK");
716 strcpy(remcomOutBuffer
,"E02");
717 debug_error("malformed write memory command: %s",remcomInBuffer
);
723 /* cAA..AA Continue at address AA..AA(optional) */
724 /* sAA..AA Step one instruction from AA..AA(optional) */
725 /* try to read optional parameter, pc unchanged if no parm */
726 ptr
= &remcomInBuffer
[1];
727 if (hexToInt(&ptr
,&addr
))
729 /* registers[PC_REGNUM].lo = addr;*/
730 fprintf (stderr
, "Setting PC to 0x%x\n", addr
);
734 if (remcomInBuffer
[0] == 's')
735 set_step_traps (frame
);
738 return RETURN_TO_PROGRAM
;
741 /* kill the program */
743 ResumeThread (mainthread
);
744 return RETURN_TO_PROGRAM
;
746 case 'q': /* Query message */
747 if (strcmp (&remcomInBuffer
[1], "Offsets") == 0)
749 sprintf (remcomOutBuffer
, "Text=%x;Data=%x;Bss=%x",
750 ldinfo
->LDCodeImageOffset
,
751 ldinfo
->LDDataImageOffset
,
752 ldinfo
->LDDataImageOffset
+ ldinfo
->LDDataImageLength
);
755 sprintf (remcomOutBuffer
, "E04, Unknown query %s", &remcomInBuffer
[1]);
759 /* reply to the request */
760 if (! putpacket(remcomOutBuffer
))
761 return RETURN_TO_NEXT_DEBUGGER
;
769 const char *bitRateString
;
772 struct bitRate bitRateTable
[] =
774 { AIO_BAUD_50
, "50" },
775 { AIO_BAUD_75
, "75" },
776 { AIO_BAUD_110
, "110" },
777 { AIO_BAUD_134p5
, "134.5" },
778 { AIO_BAUD_150
, "150" },
779 { AIO_BAUD_300
, "300" },
780 { AIO_BAUD_600
, "600" },
781 { AIO_BAUD_1200
, "1200" },
782 { AIO_BAUD_1800
, "1800" },
783 { AIO_BAUD_2000
, "2000" },
784 { AIO_BAUD_2400
, "2400" },
785 { AIO_BAUD_3600
, "3600" },
786 { AIO_BAUD_4800
, "4800" },
787 { AIO_BAUD_7200
, "7200" },
788 { AIO_BAUD_9600
, "9600" },
789 { AIO_BAUD_19200
, "19200" },
790 { AIO_BAUD_38400
, "38400" },
791 { AIO_BAUD_57600
, "57600" },
792 { AIO_BAUD_115200
, "115200" },
796 char dataBitsTable
[] = "5678";
798 char *stopBitsTable
[] = { "1", "1.5", "2" };
800 char parity
[] = "NOEMS";
802 /* Start up. The main thread opens the named serial I/O port, loads
803 the named NLM module and then goes to sleep. The serial I/O port
804 is named as a board number and a port number. It would be more DOS
805 like to provide a menu of available serial ports, but I don't want
806 to have to figure out how to do that. */
813 int hardware
, board
, port
;
819 struct debuggerStructure s
;
825 progname
= "gdbserve";
827 /* set default serial line */
832 /* set default serial line characteristics */
833 bitRate
= AIO_BAUD_9600
;
834 dataBits
= AIO_DATA_BITS_8
;
835 stopBits
= AIO_STOP_BITS_1
;
836 parityMode
= AIO_PARITY_NONE
;
839 for (argc
--, argv
++; *argv
; argc
--, argv
++)
844 if (strnicmp(*argv
, "BAUD=", 5) == 0)
849 for (brp
= bitRateTable
; brp
->bitRate
!= (BYTE
) -1; brp
++)
851 if (strcmp(brp
->bitRateString
, bp
) == 0)
853 bitRate
= brp
->bitRate
;
858 if (brp
->bitRateString
== NULL
)
860 fprintf(stderr
, "%s: %s: unknown or unsupported bit rate",
865 else if (strnicmp(*argv
, "BOARD=", 6) == 0)
868 board
= strtol (bp
, &ep
, 0);
869 if (ep
== bp
|| *ep
!= '\0')
871 fprintf (stderr
, "%s: %s: expected integer argument\n",
876 #if 1 /* FIXME: this option has been depricated */
877 else if (strnicmp(*argv
, "NODE=", 5) == 0)
880 board
= strtol (bp
, &ep
, 0);
881 if (ep
== bp
|| *ep
!= '\0')
883 fprintf (stderr
, "%s: %s: expected integer argument\n",
889 else if (strnicmp(*argv
, "PORT=", 5) == 0)
892 port
= strtol (bp
, &ep
, 0);
893 if (ep
== bp
|| *ep
!= '\0')
895 fprintf (stderr
, "%s: %s: expected integer argument\n",
911 "Usage: load %s [options] program [arguments]\n", progname
);
915 err
= AIOAcquirePort (&hardware
, &board
, &port
, &AIOhandle
);
916 if (err
!= AIO_SUCCESS
)
920 case AIO_PORT_NOT_AVAILABLE
:
921 fprintf (stderr
, "Port not available\n");
924 case AIO_BOARD_NUMBER_INVALID
:
925 case AIO_PORT_NUMBER_INVALID
:
926 fprintf (stderr
, "No such port\n");
930 fprintf (stderr
, "Could not open port: %d\n", err
);
937 err
= AIOConfigurePort (AIOhandle
, bitRate
, dataBits
, stopBits
, parityMode
,
938 AIO_HARDWARE_FLOW_CONTROL_OFF
);
940 if (err
== AIO_QUALIFIED_SUCCESS
)
942 AIOPORTCONFIG portConfig
;
944 fprintf (stderr
, "Port configuration changed!\n");
946 portConfig
.returnLength
= sizeof(portConfig
);
947 AIOGetPortConfiguration (AIOhandle
, &portConfig
, NULL
);
950 " Bit Rate: %s, Data Bits: %c, Stop Bits: %s, Parity: %c,\
952 bitRateTable
[portConfig
.bitRate
].bitRateString
,
953 dataBitsTable
[portConfig
.dataBits
],
954 stopBitsTable
[portConfig
.stopBits
],
955 parity
[portConfig
.parityMode
],
956 portConfig
.flowCtrlMode
? "ON" : "OFF");
958 else if (err
!= AIO_SUCCESS
)
960 fprintf (stderr
, "Could not configure port: %d\n", err
);
961 AIOReleasePort (AIOhandle
);
965 if (AIOSetExternalControl(AIOhandle
, AIO_EXTERNAL_CONTROL
,
966 (AIO_EXTCTRL_DTR
| AIO_EXTCTRL_RTS
))
969 LONG extStatus
, chgdExtStatus
;
971 fprintf (stderr
, "Could not set desired port controls!\n");
972 AIOGetExternalStatus (AIOhandle
, &extStatus
, &chgdExtStatus
);
973 fprintf (stderr
, "Port controls now: %d, %d\n", extStatus
,
977 /* Register ourselves as an alternate debugger. */
978 memset (&s
, 0, sizeof s
);
979 s
.DDSResourceTag
= ((struct ResourceTagStructure
*)
980 AllocateResourceTag (GetNLMHandle (),
983 if (s
.DDSResourceTag
== 0)
985 fprintf (stderr
, "AllocateResourceTag failed\n");
986 AIOReleasePort (AIOhandle
);
989 s
.DDSdebuggerEntry
= handle_exception
;
990 s
.DDSFlags
= TSS_FRAME_BIT
;
992 err
= RegisterDebuggerRTag (&s
, AT_FIRST
);
995 fprintf (stderr
, "RegisterDebuggerRTag failed\n");
996 AIOReleasePort (AIOhandle
);
1000 /* Get the command line we were invoked with, and advance it past
1001 our name and the board and port arguments. */
1002 cmdlin
= getcmd ((char *) NULL
);
1003 for (i
= 0; i
< cmdindx
; i
++)
1005 while (! isspace (*cmdlin
))
1007 while (isspace (*cmdlin
))
1011 /* In case GDB is started before us, ack any packets (presumably
1012 "$?#xx") sitting there. */
1013 if (! putDebugChar ('+'))
1015 fprintf (stderr
, "putDebugChar failed\n");
1016 UnRegisterDebugger (&s
);
1017 AIOReleasePort (AIOhandle
);
1021 mainthread
= GetThreadID ();
1023 if (remote_debug
> 0)
1024 ConsolePrintf ("About to call LoadModule with \"%s\" %08x\r\n",
1025 cmdlin
, __GetScreenID (GetCurrentScreen()));
1027 /* Start up the module to be debugged. */
1028 err
= LoadModule ((struct ScreenStruct
*) __GetScreenID (GetCurrentScreen()),
1029 (BYTE
*)cmdlin
, LO_DEBUG
);
1032 fprintf (stderr
, "LoadModule failed: %d\n", err
);
1033 UnRegisterDebugger (&s
);
1034 AIOReleasePort (AIOhandle
);
1038 /* Wait for the debugger to wake us up. */
1039 if (remote_debug
> 0)
1040 ConsolePrintf ("Suspending main thread (%08x)\r\n", mainthread
);
1041 SuspendThread (mainthread
);
1042 if (remote_debug
> 0)
1043 ConsolePrintf ("Resuming main thread (%08x)\r\n", mainthread
);
1045 /* If we are woken up, print an optional error message, deregister
1046 ourselves and exit. */
1047 if (error_message
!= NULL
)
1048 fprintf (stderr
, "%s\n", error_message
);
1049 UnRegisterDebugger (&s
);
1050 AIOReleasePort (AIOhandle
);