Вложений: 11
Инструменты для отладки макросов в панели СП3хх
Может кому-то пригодится.
Что делать начинающему программеру на Си, как отладить свой макрос, найти логические ошибки?
Конечно если макрос из пяти строчек кода, то и отлаживать тут особо нечего, а если он большой и если еще будет куча функций в Глобальном макросе? Можно "натыкать" по ходу текста макросов вспомогательных регистров PSW и записывать в них промежуточные результаты, на экранах наделать "левых" дисплеев с этими PSW, а после отладки искать их и удалять.
Предлагаю цивилизованный способ, при котором после отладки ничего искать и удалять ненужно. Кроме того при отладке появятся точки останова.
1. Понадобится окно для вывода отладочной информации. Во вложении есть несколько образцов таких окон.
Вложение 31792 Вложение 31793
Принцип построения такого окна простой. Это
- несколько текстовых дисплеев, расположенных друг под другом. Количество регистров в дисплее желательно делать кратным 4 для корректной работы в последующем символов табуляции;
- адрес регистра первого дисплея - PSW0, с динамической адресацией через регистр PSW139
Вложение 31782
почему сделано так, будет понятно после рассмотрения работы функции вывода отладочной информации на экран.
- номера регистров последующих дисплеев являются продолжением регистров пердыдущих
Вложение 31783 Вложение 31784
шрифт для дисплеев нужно выбирать моноширинный, у меня это - "Fixedsys".
- поверх дисплеев расположены "невидимые" битовые кнопки PSW136.0, PSW136.1, PSW136.2
Вложение 31785 Вложение 31786
Вложение 31787 Вложение 31788
2. Кроме окна нужна функция, которая и будет выводить отладочную информации на экран.
Вложение 31791
Помешаем в Глобальном макросе, например, в самом конце такую функцию
Код:
/* DEBUG Print */
void realdprintf (char const *file, int line, char const *func, unsigned delay, char const *format, ...)
{
#define winNo 5004 //Debug window number
#define LTOTAL 6 //Number of lines in the debug window
#define CTOTAL 56 //Number of characters in one line of the debug window
#define INPUT PSW[136] //Window control buttons
#define SCRADDR PSW[139] //Initial address of the screen buffer
#define TIC (*(DWORD*) (PSW + 38)) //System timer
#define TABS 8
#define OK 0
#define LEFT 1
#define RIGHT 2
#define KbdStatus(bitno) ((INPUT) & (1 << (bitno)))
#define FORWARD 1
#define BACKWARD -1
char *pBuffScr;
char *pBuffPrint = Malloc((LTOTAL -1) * CTOTAL);
BYTE *saveREGS = Malloc(LTOTAL * CTOTAL);
WORD saveREG1, saveREG2;
static int Xpos = 0, Ypos = 0;
register int t, len2;
int len1, len3, dir;
unsigned long tm;
va_list arg;
/* Save working registers */
saveREG1 = INPUT;
saveREG2 = SCRADDR;
/* Calculate the initial address of the screen buffer */
SCRADDR = PSW[10] - LTOTAL * CTOTAL/2;
pBuffScr = (char*) &PSW[SCRADDR];
/* Save the registers of the screen buffer area */
memcpy(saveREGS, pBuffScr, LTOTAL * CTOTAL);
/* Clear screen buffer */
memset(pBuffScr, ' ', LTOTAL * CTOTAL);
/* Print to screen line #1 */
len1 = sprintf(pBuffScr, "> %s line %d in %s ", func, line, file);
len2 = CTOTAL + Max(0, len1-CTOTAL);
/* remaining print lines in the buffer */
va_start(arg, format);
len3 = vsnprintf (pBuffPrint, LTOTAL * CTOTAL - len2 - 1, format, arg);
va_end(arg);
pBuffPrint[len3] = '\0';
/* Display buffer on screen */
t= 0;
do {
switch(pBuffPrint[t]) {
case '\a': //Bell
Beep();
break;
case '\t': //Tab
len2 = len2 - len2 % TABS + TABS;
break;
case '\n': //LF
len2 = len2 - len2 % CTOTAL + CTOTAL;
break;
default:
pBuffScr[len2] = pBuffPrint[t];
len2++;
}
t++;
} while (pBuffPrint[t] !=0 && len2 < (LTOTAL * CTOTAL));
/* Call the debug window and control its position */
dir = FORWARD;
tm = TIC;
while(!(KbdStatus(OK) || (delay !=0 && (TIC-tm) >= delay*10)) ) {
if (KbdStatus(RIGHT)) {
tm = TIC;
CloseWindow(winNo);
Xpos = Xpos + 100;
}
if (KbdStatus(LEFT)) {
tm = TIC;
CloseWindow(winNo);
Xpos = Xpos - 100;
if (Xpos < 0) {
Xpos = 0;
Ypos = Ypos + dir * 100;
}
if (Ypos == 400) dir = BACKWARD;
if (Ypos == 0) dir = FORWARD;
}
OpenWindow(winNo, Xpos, Ypos);
INPUT = 0;
Delay(10);
}
CloseWindow(winNo);
/* Restore working registers */
memcpy(pBuffScr, saveREGS, LTOTAL * CTOTAL);
INPUT = saveREG1;
SCRADDR = saveREG2;
/* Deallocate memory blocks */
Free(pBuffPrint);
Free(saveREGS);
}
А в самом начале Глобального макроса записываем следующие директивы препроцессора и прототип нашей функции
Код:
#define DEBUG_ENB 1
#if DEBUG_ENB
#include <stdarg.h>
#define DEBUG(...) realdprintf(__FILE__, __LINE__, __func__, __VA_ARGS__)
#else
#define DEBUG(...)
#endif
void realdprintf (char const *file, int line, char const *func, unsigned delay, char const *format, ...);
Между этими двумя фрагментами можете как обычно объявлять свои глобальные переменные и помещать свои глобальные функции.
Теперь как это работает.
В нужном месте любого своего макроса помещаете вызов такой "функции"
DEBUG(int delay, const char *format, ... ), где
- delay - время "показа" окна отладки в сек
- format - строка формата аналогичная функции fprint, в строке формата можно использовать \n - переход на новую строку, \t - табуляция, \a - короткий бип
- ... собственно сами переменные, которые нужно вывести на экран.
Например,
int a, b, c;
a= 2;
b = PSW[250];
c = a + b;
DEBUG(20, " This is my test\n a= %d b= %d c= %d", a, b, c);
PSW[300] = 100;
PSW[400] = PSW[256] + PSW[300];
DEBUG(30, "PSW[256]= %d PSW[300]= %d PSW[400]= %d", PSW[256], PSW[300], PSW[400]);