Now I have written a very basic debugging environment that exploits the Hatari native features for output
i have:
- variable traces (needs Atari-side code with mapping)
- assertions with useful debug information
- basic print and breakpoint support
Planned:
- nicer coredump output
- exceptions handlers so that 2,3, or 4 bombs call the debugger
The whole thing is for gcc without MintLib (hence custom string functions) but you'll get the point:
Public header:
Code: Select all
/************************************************************
* Atari ST Harddisk Menu by Paradize *
************************************************************
* @file natfeats_debug.h
* @author Simon Sunnyboy / Paradize <marndt@asmsoftware.de>
* @copyright Paradize http://paradize.atari.org/
************************************************************
* @brief API for debugging with Hatari native features
*/
#ifndef NATFEATS_DEBUG_H
#define NATFEATS_DEBUG_H
#include <stdint.h>
typedef enum
{
CD_I8,
CD_U8,
CD_I16,
CD_U16,
CD_I32,
CD_U32,
CD_BOOL,
CD_STRING
} Coredump_id_e;
void DBG_AddTrace(const char * varname, Coredump_id_e id, void * dptr);
void DBG_ClearTrace(void);
#ifdef USE_NATFEATS
void DBG_Init(void);
void DBG_Print(const char *str);
void DBG_Breakpoint(void);
void DBG_Coredump(void);
void DBG_assert(const char *cond, const char * filename, const uint16_t line);
#define assert(x) (((x) == 0) ? DBG_assert(#x, __FILE__, __LINE__):(void)0)
#else
#define DBG_Coredump() ((void)0)
#define DBG_Init() ((void)0)
#define DBG_Print(x) ((void)0)
#define DBG_Breakpoint() ((void)0)
#define assert(x) ((void)0)
#endif
#endif // NATFEATS_DEBUG_H
Main implementation (needs Eero's Natfeats access assembly source):
Code: Select all
/************************************************************
* Atari ST Harddisk Menu by Paradize *
************************************************************
* @file natfeats_debug.c
* @author Simon Sunnyboy / Paradize <marndt@asmsoftware.de>
* @author Eero Tamminen (Hatari natfeats access API)
* @copyright Paradize http://paradize.atari.org/
************************************************************
* @brief main() function and handling
*/
#include <stdint.h>
#include <stdbool.h>
#include <mint/osbind.h>
/* public interface */
#include "natfeats_debug.h"
#include "string_lib.h"
/* internal ASM helper interface for natfeats.c */
extern int32_t nf_id(const char *);
/* it's best not to use this directly as arguments are untyped */
extern int32_t nf_call(int32_t ID, ...);
/* call only from Supervisor mode */
extern int16_t detect_nf(void);
#define COREDUMP_MAX_ENTRIES 50
typedef struct
{
char entry[20]; /**< variable name */
Coredump_id_e id; /**< id tag to identify coredump entry */
void *dptr; /**< pointer to data */
} Coredump_t;
static uint16_t coredump_nr_entries = 0;
Coredump_t coredump[COREDUMP_MAX_ENTRIES];
#define NATFEATS_UNDETECTED -1
/* NatFeats available & initialized */
static int16_t nf_ok = NATFEATS_UNDETECTED;
/* handles for NF features that may be used more frequently */
static int32_t nfid_print, nfid_debugger;
/* API documentation is in natfeats.h header */
static void nf_init(void)
{
if(nf_ok == NATFEATS_UNDETECTED)
{
void *sup = (void*)Super(0);
nf_ok = detect_nf();
Super(sup);
if (nf_ok) {
/* initialize commonly used handles */
nfid_print = nf_id("NF_STDERR");
nfid_debugger = nf_id("NF_DEBUGGER");
}
}
return;
}
void DBG_AddTrace(const char * varname, Coredump_id_e id, void * dptr)
{
if(coredump_nr_entries < COREDUMP_MAX_ENTRIES)
{
StrCpy(coredump[coredump_nr_entries].entry, (char *)varname);
coredump[coredump_nr_entries].id = id;
coredump[coredump_nr_entries].dptr = dptr;
coredump_nr_entries++;
}
}
void DBG_ClearTrace(void)
{
coredump_nr_entries = 0;
}
#ifdef USE_NATFEATS
#pragma message(" -- debugging with native features enabled")
static char numbers[10];
static char * U32_ASCII(const uint32_t val)
{
uint16_t idx = 9;
uint32_t tmp = val;
numbers[9]='\0';
do
{
idx--;
numbers[idx] = '0' + (uint8_t)(tmp % 10);
tmp /= 10;
}
while((tmp > 0)||(idx == 0));
return &numbers[idx];
}
static char * I32_ASCII(const int32_t val)
{
uint16_t idx = 9;
int32_t tmp = val;
bool sign = false;
if(tmp < 0)
{
sign = true;
tmp = -val;
}
numbers[9]='\0';
do
{
idx--;
numbers[idx] = '0' + (uint8_t)(tmp % 10);
tmp /= 10;
}
while((tmp > 0)||(idx == 1));
if(sign == true)
{
idx--;
numbers[idx] = '-';
}
return &numbers[idx];
}
void DBG_Coredump(void)
{
uint16_t idx = 0;
DBG_Print("\r\n---- COREDUMP ------------------------------------------------------------\r\n");
while(idx < coredump_nr_entries)
{
DBG_Print(coredump[idx].entry);
DBG_Print(" = ");
switch(coredump[idx].id)
{
case CD_I8:
{
int8_t val = *(int8_t *)coredump[idx].dptr;
DBG_Print(I32_ASCII(val));
}
break;
case CD_I16:
{
int16_t val = *(int16_t *)coredump[idx].dptr;
DBG_Print(I32_ASCII(val));
}
break;
case CD_I32:
{
int32_t val = *(int32_t *)coredump[idx].dptr;
DBG_Print(I32_ASCII(val));
}
break;
case CD_U8:
{
uint8_t val = *(uint8_t *)coredump[idx].dptr;
DBG_Print(U32_ASCII(val));
}
break;
case CD_U16:
{
uint16_t val = *(uint16_t *)coredump[idx].dptr;
DBG_Print(U32_ASCII(val));
}
break;
case CD_U32:
{
uint32_t val = *(uint32_t *)coredump[idx].dptr;
DBG_Print(U32_ASCII(val));
}
break;
case CD_BOOL:
{
bool val = *(bool *)coredump[idx].dptr;
if(val == true)
{
DBG_Print("TRUE");
}
else
{
DBG_Print("FALSE");
}
}
break;
case CD_STRING:
{
char * to_w = (char *)coredump[idx].dptr;
DBG_Print(to_w);
}
break;
default:
DBG_Print("<undefined id>");
break;
}
DBG_Print("\r\n");
idx++;
}
}
void DBG_Init(void)
{
nf_init();
}
void DBG_Print(const char *str)
{
nf_init();
if (nfid_print)
{
(void)nf_call(nfid_print, str);
}
}
void DBG_Breakpoint(void)
{
nf_init();
if (nfid_debugger)
{
DBG_Print("\r\n---- DEBUGGER ENTRY ---------------------------------------------------\r\n");
(void)nf_call(nfid_debugger);
}
}
void DBG_assert(const char *cond, const char * filename, const uint16_t line)
{
DBG_Print("\r\n---- ASSERTION FAILED -------------------------------------------------\r\n");
DBG_Print("Assertion failed: ");
DBG_Print(cond);
DBG_Print("\r\nFile: ");
DBG_Print(filename);
DBG_Print("\r\nLine: ");
DBG_Print(U32_ASCII(line));
DBG_Coredump();
DBG_Breakpoint();
}
#endif // USE_NATFEATS
The code:
- calls DBG_Init(9 once
- uses assert() like assert.h would be used
- DBG_AddTrace() to add variables to the coredump
- DBG_ClearTrace() to clear them (can be also used to dump locals if stack order is maintained
- DBG_Coredump(9 to force a nice output to the Hatari debugger window
Example code from my current project:
Code: Select all
static DB_state DB_ParseCSV(int32_t handle)
{
DB_state st = DB_EMPTY;
if(handle < 0)
{
return DB_GEMDOS_ERROR;
}
int32_t ch_read;
DBG_AddTrace("ch_read", CD_I32, &ch_read); // trace a local variable
DBG_AddTrace("ProgDB_entries", CD_U16, &ProgDB_entries); // globals..
DBG_AddTrace("linebuf",CD_STRING,linebuf);
DBG_AddTrace("path prog 1",CD_STRING,ProgDB[0].pathname);
DBG_AddTrace("prog 1",CD_STRING,ProgDB[0].progname);
/* process ing loop with asserts etc omitted */
DBG_Coredump();
DBG_ClearTrace(); // we must clear the trace or the locals are messed up
return st;
}
Inside the hatari window, it will look like this:
Code: Select all
---- ASSERTION FAILED -------------------------------------------------
Assertion failed: <none>
File: /home/marndt/Projects/Atari/harddisk_menu/src/prog_database.c
Line: 170
---- COREDUMP ------------------------------------------------------------
ch_read = 0
ProgDB_entries = 4
linebuf =
path prog 1 = C:\COMMANDO\COMMANDO.PRG
prog 1 = Commando (Klapauzius)
Reading symbols from program '/home/marndt/tmp/just_delete_me/AUTO/hdmenu.prg' symbol table...
GCC/MiNT executable, GST symbol table, reloc=0, program flags: FASTLOAD TTRAMLOAD TTRAMMEM PRIVATE (0x7)
Trying to load symbol table at offset 0x13020...
NOTE: ignored 1 globally defined equated values.
NOTE: ignored 294 unnamed / local symbols (= name starts with '.L').
NOTE: ignored 17 object file names (= name has '/' or ends in '.o').
WARNING: symbols '_spw_text' & 'liblinkfile.a' have the same 0xecde address.
WARNING: symbols '_DBG_AddTrace' & 'libnatfeats_debug.a' have the same 0xee9a address.
Loaded 96 symbols from '/home/marndt/tmp/just_delete_me/AUTO/hdmenu.prg'.
CPU=$ff76, VBL=380, FrameCycles=76216, HBL=148, LineCycles=440, DSP=N/A
$0000ff76 : 4e75 rts
>
i used a forced assert here...