Gracias por los punteros, Markt y Chris-Stratton. La opción de semihosting resultó ser bastante sencilla. Logré encontrar la fuente para un par de rutinas de registro simples que pueden enviar mensajes a la consola OpenOCD. Los publicaré aquí ya que (i) requirieron alguna modificación para funcionar y (ii) creo que esta información no es muy fácil de encontrar para las personas que recién comienzan.
Primero, el código D aquí se adapta fácilmente para proporcionar la siguiente función C:
void send_command(int command, void *message)
{
asm("mov r0, %[cmd];"
"mov r1, %[msg];"
"bkpt #0xAB"
:
: [cmd] "r" (command), [msg] "r" (message)
: "r0", "r1", "memory");
}
Ejemplo de llamar a send_command para escribir una cadena en la consola OpenOCD:
const char s[] = "Hello world\n";
uint32_t m[] = { 2/*stderr*/, (uint32_t)s, sizeof(s)/sizeof(char) - 1 };
send_command(0x05/* some interrupt ID */, m);
Segundo, la función putChar dada en los comentarios aquí funciona bien, excepto que tuve que agregar un '#' antes de 0x03:
void put_char(char c)
{
asm (
"mov r0, #0x03\n" /* SYS_WRITEC */
"mov r1, %[msg]\n"
"bkpt #0xAB\n"
:
: [msg] "r" (&c)
: "r0", "r1"
);
}
Para ver la salida de estas funciones, primero inicio OpenOCD, luego me conecto usando arm-none-eabi-gdb de la siguiente manera:
target remote localhost:3333
monitor arm semihosting enable
monitor reset halt
load code.elf
continue
Tenga en cuenta que los mensajes aparecen en la salida estándar del proceso OpenOCD, no en la consola GDB.