PDA

Просмотр полной версии : Консоль в СП3хх



petera
20.05.2017, 12:19
Хочу консоль в СП3хх.
Зачем?
1. Т.к. я чайник в Си, то порой неработающие 5 строк в своей программе приводят меня в ступор. Начинается поиск места, в котором есть ошибка. Ошибки у меня элементарные, например могу перепутать & и && или = и ==. Я ведь не работал с языком Си. Второй проект на си в моей практике - это гипертерминал в панели http://www.owen.ru/forum/showthread.php?t=26719
По этому хочу точки останова в программе, мониторинг переменных и пр.
А для этого нужна консоль. Пусть даже с экранчиком на 4 строки по 16 символов.
2. Консоль нужна для реализации таких "хотелок"
- работа с файлами на флешке прямо из окна консоли, например, создание и редактирование файлов, отправка и прием файлов из ПК по протоколу Z-модем. Если в панели нет USB, то хотелось бы сделать виртуальный диск в панели для того, чтобы работать с файлами прямо из консоли ..... Тем самым уравнять панели с буковкой "Р" и без буковки.
- сделать интерпретатор скриптового языка писать скрипты прямо в окне консоли, сохранять их на виртуальном диске. Запускать скрипты по условию или по команде с консоли.
- сделать мониторинг переменных в программах и регистров панели, записывать лог в файл.
3. Если реализовать перенаправление консольного ввода/вывода на COM порт панели, то в качестве консоли можно будет использовать, например, гипертерминал Windows. Тогда все по п.2 можно будет делать удаленно.
3. наверно будут и другие хотелки.

Теперь, что для консоли нужно
1. Клавиатура и экран. Это элементарно, я уже делал в своем гипертерминале.
2. Функции для консольного ввода/вывода. Придется писать самому.
3. Придумать унифицированный интерфейс между экранами панели и функциями ввода/вывода. Предусмотреть возможность работы с разными консольными экранами с любым количеством строк и столбцов, причем так, чтобы в самих функциях ничего не менялось.

Экран консоли и клавиатуру я сделал. Часть функций написал, но не все. Т.к. я с Си не работал, то мне совсем понятно как работают некоторые реальные функции, например getchar().
Чтобы не изобретать названия для своих функций решил имена им давать заглавными буквами. Теперь и названия привычные и компилятор не ругается на использование зарезервированных имен.

Пока тестирую то, что написал, а для этого в качестве теста написал игру "Крестики-нолики"
Видео с работающей панелью мне записать нечем, по этому показываю анимацию gif
http://www.owen.ru/forum/attachment.php?attachmentid=31258&d=1495218728
В проекте один экран,
31269

Один макрос
31270

И набор функций а Глобальном макросе
31271

Для наглядности часть моих "консольных" функции поместил прямо в Глобальный макрос
31272 31273

31274 31275

31276



Хотел бы услышать конструктивные замечания и предложения по выше сказанному.

petera
23.06.2017, 14:24
Может кому-то пригодится.
Что делать начинающему программеру на Си, как отладить свой макрос, найти логические ошибки?
Конечно если макрос из пяти строчек кода, то и отлаживать тут особо нечего, а если он большой и если еще будет куча функций в Глобальном макросе? Можно "натыкать" по ходу текста макросов вспомогательных регистров 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]);

petera
23.06.2017, 14:42
Если в Глобальном макросе
#define DEBUG_ENB 1
то
при вызове макроса из примера
31795
на экране панели появится на 20 сек сначала такое окно
> Func1 line 134 in Mac355\Mac355.c
This is my test
a= 2 b= 0 c= 2

Затем, на 30 сек такое окно
> Func1 line 137 in Mac355\Mac355.c
PSW[256]= 0 PSW[400]= 100 PSW[400]= 100

Первая строка служебная, формируется автоматически и содержит название макроса, откуда вызывался DEBUG, и номер строки в скомпилированной программе.
Пока проект не закрыт, то при каждой компиляции макросов или при загрузке в панель в папке "ARMTool" конфигуратора создаются папки с компилированными макросами
31796
как раз ссылка на такой файл и присутствует в первой строке
31797

После полной отладки макросов строки DEBUG(.... можно даже не удалять из текста макросов. Достаточно в Глобальном макросе изменить директиву
#define DEBUG_ENB 0
после чего компилятор просто проигнорирует все DEBUG(.... и не будет включать нашу функцию в компиляцию.

Окна отладки имеют невидимые кнопки.По краям - влево и вправо. По центру - ОК.
Кнопками влево и вправо можно перемещать окно по экрану
Кнопка ОК принудительно закрывает окно

ЗЫ
ВАЖНО!
Режим выполнения отлаживаемого макроса, хотя бы на время отладки, должен быть установлен - "Параллельный"
После отладки режим выполнения может быть установлен как - "Последовательный"