/***************************************************************************** * File: testport.c * * Description: Contains the tport() low priority task which uses idle CPU capacity. (Entry at Line 683) * tport() watches other tasks, queues characters going to the text display(s), does input/output. * A companion low priority task in tpmain.c (tpmain() at line 223) alternates sharing CPU idle capacity. * * The ESC keyboard input character will abort any display and restart the tport() task. A second * ESC char clears the screen. "?" displays the current list of keyboard commands. * Normally the task is constantly Yielding in the absence of keyboard input characters. * * In this example, the GreenHills Systems (GHS) API uses the second SCC serial port for * keyboard input and screen output. TPort is also adaptable to run on other taskers and RTOS's. * However, it does depend on being able to read data and structures inside other tasks which can * be prevented by C++ privacy and by the MMU enforced separation of task into secure * memory regions. Some RTOS's like GHS Integrity isolate tasks, thus preventing * effective tport() monitoring of the entire application. * * Keyboard input commands invoke custom scanning displays running in the test port task. * xprintf() and tprintf() print to a character queue. tprintf() will yield if queue not empty. * tprintf() must NOT be used by any other task but tport(), otherwise it will yield the calling task! * Instead, xprintf() should be used to print outside the tport() task because xprintf() does not yield, * but harmlessly overflows its queue. If this should happen, it only messes up the display, nothing else. * Like xprintf(), iprintf() should be used inside interrupts provided the printf() libriary is re-entrant. * * Modification History: * NAME DATE DESCRIPTION * * *****************************************************************************/ #include /* GreenHills Integrity RTOS */ #include /* Artysen IBM 440 card */ #include /* for printf() ftn prototype */ #include /* for exit() ftn prototype */ #include /* for strcpy() ftn prototype */ #include #include #include #include #include "tpmacros.h" #include "tpsizes.h" #include "tptypes.h" extern struct taskitem tasktable[]; // Allocated storage extern void recordTaskExit (TASKst *tptr); extern void recordTaskEntry (TASKst *tptr); void iprintf(const char *fmt, ...); void tprintf(const char *fmt, ...); void xprintf(const char *fmt, ...); extern BOOL putQtoTP(char *charptr); //Prototypes extern BOOL getQtoTP(char *charptr); extern BOOL putQfrTP(char *charptr); extern BOOL getQfrTP(char *charptr); extern BOOL putQtoIT(char *charptr); extern BOOL getQtoIT(char *charptr); extern Queue toTPQtab; /* Tx QTab */ extern Queue frTPQtab; /* Rx QTab */ extern Queue toITQtab; /* Tx QTab */ extern Queue frITQtab; /* Rx QTab */ extern Queue toAPQtab; /* Tx QTab */ extern Queue frAPQtab; /* Rx QTab */ extern Queue toACQtab; /* Tx QTab */ extern Queue frACQtab; /* Rx QTab */ extern Queue toTCPQtab; /* Tx QTab */ extern Queue frTCPQtab; /* Rx QTab */ extern Queue jamtoACQtab; /* Tx QTab */ extern Queue txMCQtab; // Tx Event OpCode QTab void signon(void); ULONG commandlinelength; char tplinebuffer[MAX_CMD_LINE]; /* test port cmd line input buffer */ char *linebuffer; /* pointer to current line input buffer */ short int xoffstate = 0; /* xon xoff flag */ static TASKst *tp_tptr; // IODevice m_UartDev; // "HighResTimer" is Clock name in PPC440gp Time PriClk; // Structure: time_t Seconds (typedef long time_t; ** PLUM HALL wants this to be signed **) // UINT4 Fractions unsigned long error_result_GetClockTime = 0; unsigned long file_dump_flag = 0; extern void initTaskTable(TASKst *tptr); /*------------------------------------------------------------------------------------ ** Task "tport_tx_poll" is not running !! Serial Input/Output is moved into task T2000 **-----------------------------------------------------------------------------------*/ void tport_tx_poll (void) { } // service RS-232 UART transmitter. /*-----------------------------------------------------------------------------------* ** Test Port Function declarations **-----------------------------------------------------------------------------------*/ void ttag(void); //Test Port command void tptime(void); void tputc(char c); void tpsleep(void),show_keyboard_char(void); void mdump(void),movemem(void),next(void),help(void),modmem(void); void watch_cmd(void); void qstatus(void),clearstats(void); void compare_memory(void),call_to_function(void); void docrlf(BOOL tptask), hclear(void); void tertype(void); void rbin(void); void mtest(void); void exit_unload(void); void comdisplay(void); void taskcmd(void); void swsio(void); void sio_dump_on(void); void sio_dump_off(void); void file_dump(void); void watch_uart1(void); struct funcitem functable[] = { "clr", "Clear Stats", clearstats, /* clear statistics */ "cls", "Clear Screen", hclear, "com", "Communications Stats", comdisplay, /* Communications statistics. */ "cm", "compare memory", compare_memory, /* Compare memory blocks */ "d", "display memory", mdump, /* Display Memory */ // "exit", "exit & unload", exit_unload, /* Stop and unload task group */ // "fd", "file dump", file_dump, /* Dump iprintf data */ // "ifil", "fill i queue", fill_iQueue, /* fill i Queue like iprintf */ "jsr", "jsr ", call_to_function, "key", "watch keyboard", show_keyboard_char, /* Watch Keyboard */ "mm", "Modify 'mm addr {b,w,l} wo'", modmem, /* Modify Memory */ "mv", "Move 'mv source length dest'", movemem, /* MoVe a block of memory */ "mt", "RAM Mem Test", mtest, /* Asm Ram Test - wipes out memory. */ "qs", "queue status", qstatus, /* display Queue Status */ // "sws", "SWitch Serial IO", swsio, /* Toggle in main() or task2000 */ "task", "Task Counters", taskcmd, /* CPU utilization. */ "time", "Pri Timer", tptime, /* Scan display primary timer */ "ttag", "Time Tag", ttag, /* Time tag from primary timer */ "tt", "Set Terminal type", tertype, /* VT-102 or HyperTerminal */ "uart1", "Uart1 input test", watch_uart1, /* watch and print chr inputs. */ "ver", "Bios Version", signon, /* Sign on message */ "wm", "Watch 'wm addr t'(b,w,l,d,e)",watch_cmd, /* Watch for Memory changes. */ "?", "display help menu", help, /* display menu of commands */ "/", "", mdump }; /**** end of table ****/ /************************************************************************ ** print table of commands *************************************************************************/ char inwait(void); //prototype void help(void) { struct funcitem *cmdtblptr; long rowcounter = 50; //dn counter cmdtblptr = functable; while( strcmp (cmdtblptr->name,"/") ) /* strcmp == 1 (TRUE) when strings are NOT equal */ { if (--rowcounter <= 0) // Halt Display. Show no more than one 50 line screen at a time. { inwait(); // Resume next 50 lines on any key stroke. rowcounter = 50; } tprintf("\n %-6s- %s",cmdtblptr->name,cmdtblptr->description); cmdtblptr++; } } /************************************************************************** put character in toTPQ, sleeps while full **************************************************************************/ void outsleep(char c) { while ((putQtoTP(&c)) == 0) { recordTaskExit (tp_tptr); ////Yield(); //Put task to end of run queue at its priority level. //SetTaskPriority(TaskTESTPORT, PRIORITY16, FIRSTINPRIORITY); // Park tport(). Keep tport() running as first. Executing in tport() task. //SetTaskPriority(TaskTPMAIN, PRIORITY17, FIRSTINPRIORITY); // UnPark main (). Task switch to main(). It is the only task at priority 127. usleep(10000); //10 msec /*----Sleeping------*/ recordTaskEntry(tp_tptr); } } /************************************************************************** get character from fromTPQ, sleeps while empty **************************************************************************/ char insleep(void) { char c; while ((getQfrTP(&c)) == 0) { recordTaskExit (tp_tptr); ////Yield(); //Put task to end of run queue at its priority level. //SetTaskPriority(TaskTESTPORT, PRIORITY16, FIRSTINPRIORITY); // Park tport(). Keep tport() running as first. Executing in tport() task. //SetTaskPriority(TaskTPMAIN, PRIORITY17, FIRSTINPRIORITY); // UnPark main (). Task switch to main(). It is the only task at priority 127. usleep(10000); //10 msec /*----Sleeping------*/ recordTaskEntry(tp_tptr); } return(c); } unsigned short int tpinQcheck (void) { return (frTPQtab.count); } BOOL tpincheck (void) { if (tpinQcheck ()) return(FALSE); /* Invert flag for Forth */ else return(TRUE); /* Invert flag for Forth */ } /************************************************************************** put character in toTPQ, tpsleeps while full **************************************************************************/ void ouwait(char c, BOOL tptask) { while ((putQtoTP(&c)) == 0) { if (tptask == TRUE) /* No other task but tport must sleep here */ tpsleep(); else return; /* Other task dump the character */ } } /************************************************************************** get character from fromTPQ, tpsleeps while empty **************************************************************************/ char inwait(void) { char c; while ((getQfrTP(&c)) == 0) tpsleep(); return(c); } /*------------------------------- Only Non-test port task calls here. No tpsleep on full queue - dumps characters. ---------------------------------*/ void xputc(char c); //prototype void xtom(char *str) { while (*str) xputc(*str++); } /*-------------------------------*/ /* Only Test port tasks call here for string print */ /*-------------------------------*/ void ttom(char *str) { while (*str) tputc(*str++); while(toTPQtab.count) /* return after string is printed */ tpsleep(); } /*-------------------------------*/ void tom (char *str, BOOL tptask) { if (tptask) ttom (str); else xtom (str); } /*--------------------------------------------------------*/ /* ** getline - Returns an edited line of characters in str. */ ULONG getline(char *str) { unsigned long clcount = 0; /* number of characters typed in current cmd line */ char c; while ((c = inwait()) != 0x0d ) { switch (c) { case 0x08: /* if back space character */ if (clcount != 0) { /* and not at start of line */ ttom ("\x8 \x8"); /* backup destructively */ str--; clcount--; } break; case 0x01: /* control A */ break; case 0x1a: /* control z */ break; /* exit switch */ default: ouwait(c, TRUE); /* echo character to terminal */ *str++ = c; /* put char in buffer */ clcount++; } if (clcount >= MAX_CMD_LINE) { ttom("Too long"); /* Prevent crash !!! */ clcount = 0; break; /* exit while() */ } } *str = 0; /* make it a C string */ return(clcount); /* 0 = no chars */ } /************************************************************************** ** Routine used to display a hex byte (8 bits). **************************************************************************/ static char hex[] = {"0123456789ABCDEF"}; void wrhex1(BYTE *num, BOOL tptask) { union { /* Convert type with no change between unsigned char & signed char */ BYTE b; /* unsigned */ char c; /* signed */ }cvt; cvt.b = *num; /* puts the byte pointed to by num into cvt.b */ ouwait (hex[(cvt.c >> 4) & 0x0f], tptask); /* convert upper nibble */ ouwait (hex[cvt.c & 0x0f], tptask); /* convert lower nibble */ } static void hexdump(BYTE *start, long byte_count, BOOL tptask) { long i; wrhex1(start, tptask); for (i=1;i> 8; BYTE lo_byte = num & 0x00ff; wrhex1(&hi_byte, tptask); wrhex1(&lo_byte, tptask); } /* Routine used to display a hex double long word (64 bits). */ void wrhex8(BYTE *start, BOOL tptask) { hexdump(start, 8, tptask); } /* Routine used to display a hex long word (32 bits). */ void wrhex4(ULONG num, BOOL tptask) { wrhex2((WORD)(num >> 16), tptask); wrhex2((WORD)num, tptask); } void tolowercase(char *str) { /* Convert string to lower case.*/ while (*str) { *str = isalpha(*str) ? tolower(*str) : *str; str++; } } void dumpline(BYTE *start, WORD count, BOOL asciiflag, BOOL tptask) { WORD i; wrhex4((long)start, tptask); /* print start address */ ouwait(' ', tptask); hexdump(start, (long)count, tptask); /* print bytes in hex */ if (asciiflag) { /* print bytes in ascii */ tom(" ", tptask); for (i= 0; i < count; i++, start++) { if ( (*start < 0x20) || (*start > 0x7e)) /* space to little past'z'*/ ouwait('.', tptask); /* non-printing char */ else ouwait(*start, tptask); /* print ascii char */ } } } void sleep_while_txTPQ_not_empty(void); //prototype void dumpblock(BYTE *start, long count, long bytesperline, BOOL tptask, WORD show_ascii) { WORD char_count = bytesperline; /* Characters per line */ while(count>0) { docrlf(tptask); // dumpline(start, ( (bytesperline < count)? bytesperline : (WORD)count ), (bytesperline > 16 ? FALSE : TRUE) ); if ( count < bytesperline ) char_count = count; dumpline(start, char_count , show_ascii, tptask ); start += bytesperline; count -= char_count; if (tptask) sleep_while_txTPQ_not_empty(); /* sleep until queue is empty */ } } /*------------------------------------------------------------------------------* ** VT220 is XTERM defacto Standard. ** Two Character Escape Sequence Initiator is: ESC [ or 0x1b, 0x5b or "\x1b\x5b" ** ** Origin (col 1, Row 1) (not necessarily visible on screen). ** Cursor position to origin: ** esc [1;1H or "\x1b\x5B\x31\x3b\x31\x48" ** ** Erase screen from origin to end: ** ESC[2J or "\x1b\x5b\x32\x4a" ** ** Erase screen from cursor to end: ** ESC[J or "\x1b\x5b\4a" ** ESC[0J or "\x1b\x5b\x30\4a" (alternate) ** **------------------------------------------------------------------------------*/ char vt220_home[] = "\x1b\x5b\x31\x3b\x31\x48"; /* home ESC[1;1H */ char vt220_hclear[] = "\x1b\x5b\x31\x3b\x31\x48\x1b\x5b\x32\x4a"; /* clear ESC[2J or "\x1b\x5b\x32\x4a" */ char crlf[] = "\x0d\x0a"; /* cariage return line feed */ char *crlf_str = crlf; /* crlf character array pointer */ char hz_hclear[] = "\x7e\x1c"; /* Hazeltine home clear */ char hz_home[] = "\x7e\x12"; /* Hazeltine home */ char ANSI_hclear[] = "\x1b\x5B\x32\x4A"; /* home clear `ESC[1;1H' */ char ANSI_home[] = "\x1b\x5B\x31\x3B\x31\x48"; /* home `ESC[2J' */ char tv_hclear[] = "\x1b\x2a"; /* Televideo home clear */ char tv_home[] = "\x1e"; /* Televideo home */ char adm_hclear[] = "\x1e\x1c"; /* ADM 3A/5 home clear */ char adm_home[] = "\x1e"; /* ADM 3A/5 home */ char vt102_hclear[] = "\x1b\x5b\x66\x1b\x5b\x4a"; /* VT102 home clear */ char vt102_home[] = "\x1b\x5b\x66"; /* VT102 home */ //int terminaltype = ANSI; /* ANSI = VT100 ? */ //char *home_clear_str = ANSI_hclear; /* Default is the ANSI option */ //char *home_str = ANSI_home; //int terminaltype = VT102; /* Good with Tera Terminal VT102 */ //char *home_clear_str = vt102_hclear; /* Default - Set Tera Terminal */ //char *home_str = vt102_home; int terminaltype = VT220; /* XTERM default */ char *home_clear_str = vt220_hclear; /* combined home and clear strings. */ char *home_str = vt220_home; char stop_scan = 0x1b; /* ESC */ /************************************************************************** ** VT102 Home ** Cursor Position (Home) (CUP method) VT102 ** ESC [ H ** 033 133 110 Octal ** x1b x5b x4a Hex ** ** Horizontal and Vertical Position (Home) (HVP method) ** ESC [ f ** 033 133 146 ** x1b x5b x66 Hex ************************************************************************************************************** ** VT102 Clear ** Erase In Display (ED) VT102 ** ** ESC [ J or ESC [ 0 J ** 033 133 112 033 133 060 112 Erases from cursor to end of screen, including cursor position. ** x1b x5b x4a or x1b x5b x30 x4a Hex ** ** ESC [ 1 J ** 033 133 061 112 Erases from beginning of screen to cursor, including cursor position. ** x1b x5b x31 x4a Hex ** ** ESC [ 2 J ** 033 133 062 112 Erases complete display. All lines are erased and changed to single-width. Cursor does not move. ** x1b x5b x32 x4a Hex ************************************************************************** ** Set Test Port Terminal Type *************************************************************************/ void tertype(void) { tprintf("\nSelect Terminal Type:\n 1 = Hazeltine\n 2 = ANSI - NT HyperTerminal"); tprintf("\n 3 = Televideo\n 4 = ADM 3A/5\n 5 = VT102"); tprintf("\n 6 = VT220 (default)\nSelect #: "); /**sscanf(linebuffer,"%*s %d",&terminaltype);***/ getline(linebuffer); if (sscanf(linebuffer,"%d",&terminaltype) > 0) { ttom ("\nTerminal Type selected: "); switch (terminaltype) { case HAZELTINE: ttom("Hazeltine"); home_clear_str = hz_hclear; home_str = hz_home; break; case ANSI: ttom("NT HyperTerminal (ANSI)"); home_clear_str = ANSI_hclear; home_str = ANSI_home; break; case TELEVIDEO: ttom("Televideo"); home_clear_str = tv_hclear; home_str = tv_home; break; case ADM: ttom("ADM 3A/5"); home_clear_str = adm_hclear; home_str = adm_home; break; case VT102: ttom("VT102"); home_clear_str = vt102_hclear; home_str = vt102_home; break; case VT220: ttom("VT220"); home_clear_str = vt220_hclear; home_str = vt220_home; break; default: ttom("What?"); break; } } } /************************************************************************** Move cursor home and clear screen **************************************************************************/ void hclear(void) { ttom(home_clear_str); } /* ** move cursor home */ void home(void) { ttom(home_str); } /* ** do a carriage return & linefeed */ void tcrlf(void) { ttom("\n"); /* Test port task is calling */ } void xcrlf(void) { xtom("\n"); } void docrlf(BOOL tptask) { if (tptask) ttom("\n"); else xtom("\n"); } /* ** tell sender to hold up */ void xoff(BOOL tptask) { ouwait(0x13, tptask); /* control S */ if (tptask) sleep_while_txTPQ_not_empty(); /* let it get sent */ } /* ** release xoff */ void xon(BOOL tptask) { ouwait(0x11, tptask); /* control Q */ if (tptask) sleep_while_txTPQ_not_empty(); /* let it get sent */ } static void prompt(void) //Must be static!!! NH { ttom("\n>>"); } void what(void) { ttom("What?\n"); } void tspace(void) { ttom(" "); } /*---------------------------------------------------------------------------------- The INTROL sscanf() library function does not work the same as other compilers. March 31, 2003 N.H. For Example, sscanf(linebuffer,"%*s %lx %lx %hx",&md_start,&md_count,&bytesperline) fails to skip over the command string (%*s) to find and load the parameters!!! Hence, the following fix. linebuffer is restored in the tport FOREVER loop. ------------------------------------------------------------------------------------*/ BOOL point_to_parameters(void) /* inc linebuffer pointer past command string. */ { short i; // xprintf ("\ntplinebuffer@point1=%s", tplinebuffer); /* display buffer contents. */ // xprintf ("\nlinebuffer@point1=%s", linebuffer); /* display buffer contents. */ for (i=0; (i < commandlinelength) && (i < 80); i++) { if (*linebuffer == 0 ) return (FALSE); /* No Parameters */ // xprintf ("\nlinebuffer@point2=%s", linebuffer); /* display buffer contents. */ if (*linebuffer == ' ') { /* find delimiting character */ linebuffer++; /* step past delimit char */ // xprintf ("\nlinebuffer@point3=%s", linebuffer); /* display buffer contents. */ return (TRUE); /* the global linebuffer is passed. */ } linebuffer++; /* Point to parameter if any. */ } return (FALSE); /* Exception case. */ } /************************************************************************** Accept command line and jump to appropriate handler **************************************************************************/ void docmd(char *cmdline, struct funcitem *cmdtable) { char str[MAX_CMD_LINE]; int i; sscanf(cmdline,"%s",str); /* get command name */ tolowercase(str); for (i=0; ;i++) { if (strcmp(cmdtable[i].name,"/") == 0) { ttom(" What?"); return; } if (strcmp(cmdtable[i].name,str) == 0) { linebuffer = &tplinebuffer[0]; /* Init for point_to_parameters() */ point_to_parameters(); /* inc linebuffer pointer past command string. INTROL fix Mar 2003 N.H. */ (*cmdtable[i].func)(); /* execute test port function */ return; } } } /************************************************************************** **************************************************************************/ unsigned long tportabort = 0; void keyboard_cmd_input(void) { commandlinelength = getline(linebuffer); if (commandlinelength < MAX_CMD_LINE) /* prevent crash!!! */ { if ( commandlinelength ) { tportabort = 0; docmd(linebuffer,functable); } } prompt(); } void signon(void) { xprintf("\ntport() task Started"); xprintf("\nFirst Good 6-11-2011"); xprintf("\nUses Serial #0"); //Serial port on PPC440GP) xprintf("\ntport() uSleep(10000)\n"); //Tasking scheme implemented } /*------------------------------------------------------------------*/ void idle (void) {} void (*tportlist[])(void) = /* array of pointers to functions */ { signon, idle, idle }; /*------------------------------------------------------------------* ** Test Port Task Entry point. **------------------------------------------------------------------*/ static Address tport_start; static Address tport_stack; static Task task_tport = 0; static Task TPORT; void tport(void) { unsigned long i; Queue *Qptr; if (task_tport == 0) // Initialize once on first execution, not on ESC restart. { //#define TPORT TaskObjectNumber(18) From included file: tport5_integrate.h generated by INTEGRATE!!! task_tport = TPORT = CurrentTask(); //Task Object Number; ReadTaskProgramCounter(TPORT, &tport_start); // Used to Restart the task on ESC input. ReadTaskStackPointer (TPORT, &tport_stack); // result ignored. tp_tptr = &tasktable[TESTPORT]; recordTaskEntry(tp_tptr); //Initial recording before first Exit. } if (file_dump_flag == TRUE) { file_dump_flag = FALSE; Qptr = &toITQtab; //Clean up after escape from "fd" command sio_dump_off(); //Halt file dump on ESC char if running. Qptr->count = 0; //Clear Queue. } for (i=0; i<2; i++) { (*tportlist[i])(); /*execute the function pointed to by index */ } tportlist[0] = prompt; /* Remove Version signon, install prompt */ tportlist[1] = idle; /* Remove any one-time function execution. */ /*------------------------------------------------------------------** ** The sleeping, or parking, or yielding OS mechanism releases the ** task at sites many levels beneath this top level site. ** ** Keyboard "ESC" input restores the task stack to the top, ** re-enters rport() to land here once again waiting for a ** new keyboard input command. **------------------------------------------------------------------*/ FOREVER /* restore global linebuffer pointer. */ { /*linebuffer is incrimented past command string. */ linebuffer = &tplinebuffer[0]; keyboard_cmd_input(); } /*------------------------------------------------------------------*/ } /************************************************************************** Keyboard hit. Return: c = 00 if no new character, else c = chr **************************************************************************/ static unsigned char kbhit(void) /* keyboard hit */ { char c; if (!getQfrTP(&c)) return (FALSE); /* no character available */ return(c); } /************************************************************************** ** Put the testport task to sleep for awhile. ** Upon wakeup, check for abort while asleep. **************************************************************************/ void tpsleep(void) { recordTaskExit (tp_tptr); ////Yield(); //Put task sleep. Put at end of run queue at its priority level. //SetTaskPriority(TaskTESTPORT, PRIORITY16, FIRSTINPRIORITY); // Park tport(). Keep tport() running as first. Executing in tport() task. //SetTaskPriority(TaskTPMAIN, PRIORITY17, FIRSTINPRIORITY); // UnPark main (). Task switch to main(). It is the only task at priority 127. usleep(10000); //10 msec //usleep(1000); //1 msec /*----Sleeping------*/ recordTaskEntry(tp_tptr); if (tportabort == 1) { /* check for 1st escape */ tportabort = 2; /* restart(tport); ** restart tport (stops output) */ WriteTaskProgramCounter(task_tport, tport_start); // start of task. WriteTaskStackPointer (task_tport, tport_stack); // top of stack tport(); // call tport() from within tport() } // never returns from this call. if (tportabort >= 3) /* check for 2nd escape */ { tportabort = 0; hclear(); prompt(); } } void sleep_while_txTPQ_not_empty(void) { while (toTPQtab.count != 0) /* typically, sleep each line until */ { tpsleep(); /* queue is empty */ } } /************************************************************************** ** "KEY' Learn what hex code the keyboard is sending **************************************************************************/ void show_keyboard_char (void) { BYTE c; FOREVER { c = inwait(); wrhex1 (&c, TRUE); tspace (); } } /*-------------------------------*/ void tplace(WORD tab) { WORD i; for (i=0; iQsize,\ Qptr->count,\ Qptr->maxcount,\ Qptr->overflowcount,\ Qptr->trafficcount); sleep_while_txTPQ_not_empty(); /* sleep until queue is empty */ } void qstatus(void) //Keyboard command "qs" at line 120 executes here. { hclear(); tprintf("\n size depth max overflows traffic"); FOREVER { home(); tcrlf(); tcrlf(); qdump("frTPQ",&frTPQtab); qdump("toTPQ",&toTPQtab); qdump("toITQ",&toITQtab); qdump("frAPQ",&frAPQtab); qdump("toAPQ",&toAPQtab); qdump("frACQ",&frACQtab); qdump("toACQ",&toACQtab); qdump("frTCPQ",&frTCPQtab); qdump("toTCPQ",&toTCPQtab); qdump("MgCode",&txMCQtab); } } /*********************************************************************** ** Display a set of variables continuously. ** ** Call with scantable = ScanItem table such as the CDisplayTable above. ** ************************************************************************/ void doint(short *wptr) { tprintf("%10d",*wptr); } void douint(unsigned short *wptr) { tprintf("%10u",*wptr); } void dolong(long *lptr) { tprintf("%10l",*lptr); } void doulong(unsigned long *lptr) { tprintf("%10lu",*lptr); } void dou10(unsigned long *lptr) { unsigned long data; data = *lptr & 0x000fffff; tprintf("%10lu",data); } void dowrhex4(long *lptr) { ttom(" "); wrhex4(*lptr, TRUE); } void dowrhex2(short int *wptr) { ttom(" "); wrhex2(*wptr, TRUE); } void dowrhex1(BYTE *bptr) { ttom(" "); wrhex1(bptr, TRUE ); /* Pass the pointer */ } void dodouble(double *dptr) { tprintf("%12.1f",*dptr); } /************************************************************************* ** "dm" Keyboard command to dump a block of memory ** "dm xxxx yyyy ccx" ** xxxx = starting address (or symbol name) ** yyyy = byte count (optional, default is 128) ** cc = bytes per line (optional, default is 16) ** x = if x is a space then scanning is enabled **************************************************************************/ static BYTE *md_start; /* Values don't have to be typed in every time */ static long md_count = 128; static long bytesperline = 16; /* Remember the last values used */ void mdump(void) { short md_scanning = 0; /* linebuffer points past command string to first parameter. */ sscanf(linebuffer,"\n%lx %lx %lx",&md_start,&md_count,&bytesperline); tprintf ("\n%lx %lx %lx", md_start, md_count, bytesperline); if ( tplinebuffer[commandlinelength -1] == ' ') md_scanning = 1; if (md_scanning) hclear(); /* home up and clear if in scan mode */ FOREVER { dumpblock(md_start, md_count, bytesperline, TRUE, TRUE); /* sleeps */ if (md_scanning) home(); /* back to home if scanning */ else return; } } /************************************************************************** ** Jump to memory address (Call) In C there is no way to declare a varable to be a function pointer directly. Instead, a character pointer is declared and a cast is used to assign the address of the function to the character pointer. int jmpadr() is declared as a function so the compiler will know that its name is a function pointer See Reference Books: 1. Turbo C P 162 - 165 2. K&R C Reference P 116 3. Borland C++ Ver5 Prog. Guide P 53 ***************************************************************************/ //DEBUG Bloodshed does not compile -->> See line 1159 for good working example! //void (*calltable[])(void) ={ /*array of pointers to functions*/ // (void *)ignore, // (void *)ignore //}; void call_to_function(void) { ULONG *x; sscanf(linebuffer,"%lx",&x); //DEBUG Bloodshed does not compile -->> See line 1159 for good working example! // calltable[0] = (void *) x; /*What a way to make 'C' execute a function! */ //DEBUG (*calltable[0])(); /*execute the function pointed to by index */ /* See ~line 1070 for pointer to funnction. */ } /************************************************************************** ** "exit" command - Stop and unload module **************************************************************************/ void exit_unload(void) { Exit(0); } /************************************************************************** ** "wm" Keyboard command to Watch Memory - Test port command "wm xxxx w" ** xxxx = starting address (Requires even address except for byte) ** 'b' = 1; Byte (1 Byte ) ** 'w' = 2; Word (2 Bytes) Default ** 'l' = 4; Long (4 Bytes) ** 'd' = 8; Double (8 Bytes) ** 'e' = 16; Extended (16 Bytes) ***************************************************************************/ static WORD *wstart; /* Initially pointing to zero is okay */ static ULONG *lstart; static msg8 *m8start; static msg16 *m16start; static long mmode = 2; /* byte = 1, word = 2, long word = 4 */ static BYTE *cstart; static char cmode = '.'; /* Mode is remembered if no new "B", "W", "L" is given */ ULONG MakeEven(ULONG num) { ULONG temp = num; temp &= 0x00000001; num &= 0xfffffffe; /* Rid of odd lsb address */ if (temp) { tprintf (" Changed WM adr to even=%x",num); cstart = (BYTE *)num; } tcrlf(); return (num); } /*------------------*/ void watch_cmd(void) { BYTE lastcharvalue; WORD lastwordvalue; ULONG lastulongvalue; msg8 lastm8; msg16 lastm16; short count; ULONG evennum; sscanf(linebuffer,"%x %c", &cstart, &cmode ); if ( cmode == 'b') mmode = 1; /* Byte (1 Byte ) */ if ( cmode == 'w') mmode = 2; /* Word (2 Bytes) */ if ( cmode == 'l') mmode = 4; /* Long (4 Bytes) */ if ( cmode == 'd') mmode = 8; /* Double (8 Bytes) */ if ( cmode == 'e') mmode = 16; /* Extended (16 Bytes) */ switch (mmode) { case 1: tcrlf(); dumpline(cstart,mmode,0, TRUE); tcrlf(); count = 24; /* bytes per tport line */ FOREVER { lastcharvalue = *cstart; while (lastcharvalue == *cstart) tpsleep(); wrhex1(cstart, TRUE); if (--count <= 0) { tcrlf(); count = 24; } else ouwait(' ', TRUE); } case 2: wstart = (WORD *)cstart; evennum = MakeEven( (ULONG)wstart); wstart = (WORD *)evennum; dumpline(cstart,mmode,0, TRUE); tcrlf(); count = 16; /* words per tport line */ FOREVER { lastwordvalue = *wstart; while (lastwordvalue == *wstart) { tpsleep(); } wrhex2(*wstart, TRUE); if (--count <= 0) { tcrlf(); count = 16; } else ouwait(' ', TRUE); } case 4: lstart = (ULONG *)cstart; evennum = MakeEven( (ULONG)lstart); lstart = (ULONG *)evennum; dumpline(cstart,mmode,0, TRUE); tcrlf(); count = 8; /* longs per tport line */ FOREVER { lastulongvalue = *lstart; while (lastulongvalue == *lstart) tpsleep(); wrhex4(*lstart, TRUE); if (--count <= 0) { tcrlf(); count = 8; } else ouwait(' ', TRUE); } case 8: /* User must type in even addresses! */ tcrlf(); dumpline(cstart,mmode,0, TRUE); tcrlf(); count = 4; /* per tport line */ m8start = (msg8 *)cstart; FOREVER { lastm8 = *m8start; while (memcmp(&lastm8, m8start, 8) == 0) { tpsleep(); } wrhex4(*(long *)m8start, TRUE); wrhex4(*(long *)((long*)m8start+1), TRUE); if (--count <= 0) { tcrlf(); count = 4; } else ouwait(' ', TRUE); } case 16: /* User must type in even addresses! */ tcrlf(); dumpline(cstart,mmode,0, TRUE); tcrlf(); count = 2; /* per tport line */ m16start = (msg16 *)cstart; FOREVER { lastm16 = *m16start; while (memcmp(&lastm16, m16start, 16) == 0) { tpsleep(); } wrhex4(*(long *)m16start, TRUE); wrhex4(*(long *)((long*)m16start+1), TRUE); wrhex4(*(long *)((long*)m16start+2), TRUE); wrhex4(*(long *)((long*)m16start+3), TRUE); if (--count <= 0) { tcrlf(); count = 2; } else ouwait(' ', TRUE); } } } /*----------------------------------------------------------------- ** "mm" Keyboard command to Modify Memory ** "mm xxxx cc w" ** xxxx = starting address ** cc = operand length (1, 2, 4 bytes) ** w = write only memary? (yes if w!=0) -------------------------------------------------------------------*/ static char *mm_start, *mm_addr; /* Pointers initially at zero */ static long mm_mmode = 2; /* byte, word, long word */ static long mm_wr_only; /* default: 0 = read/write */ static char mm_c = 'w'; /* Mofification type is remembered when new "B", "W", "L" is given */ void modmem(void) { char *temp_adr; long *ltmp; long value; ULONG vars_input; char c; temp_adr = mm_start; vars_input = sscanf(linebuffer,"%x %c %x", &mm_start, &mm_c, &mm_wr_only); tprintf("\nAdr=%x, Size=%c, WriteOnly=%d", mm_start, mm_c, mm_wr_only); switch (mm_c) /* Regardless */ { case 'l': mm_mmode=4; break; case 'w': mm_mmode=2; break; case 'b': mm_mmode=1; break; default: mm_mmode=2; break; /* mm_mmode unmodified */ } if (vars_input >= 1) /* Verify with user only New address inputs. */ { if (((ULONG)mm_start > 0x80000) || ((ULONG)mm_start < 10000)) /* Check range */ { tprintf ("\nAdr=%x is ouside normal RAM range (%x - %x hex).\nX (exit) CR (continue)", mm_start, 0, 0x80000); while ( getQfrTP(&c) == 0) /* No Rx Keyboard char's? */ { tpsleep(); /* Wait for keyboard entry. */ } if ( (c == 'x') || (c == 'X') ) /* anything else is a go-ahead */ { mm_start = temp_adr; /* Restore remembered address */ return; /* input error exit. Try again */ } } } mm_addr = mm_start; FOREVER { tcrlf(); if(mm_wr_only == 0) dumpline((BYTE *)mm_addr, (WORD)mm_mmode,1, TRUE); else wrhex4((long)mm_addr, TRUE); /* print start address */ ttom(" ?"); linebuffer = &tplinebuffer[0]; /* Restore pointer to base. */ if (getline(linebuffer)) /* no return until CR */ { switch (*linebuffer) { case 'l': mm_mmode = 4; break; case 'w': mm_mmode = 2; break; case 'b': mm_mmode = 1; break; case '-': mm_addr -= mm_mmode; break; case '@': ltmp = (long *)mm_addr; /* 'Handle' Go to modify what is pointed to. */ ltmp = (long *)*ltmp; mm_addr = (char *)ltmp; break; case '.': return; default: { if (sscanf(linebuffer, "%x", &value)) { switch (mm_mmode) { case 1: *mm_addr = (char)value; mm_addr += mm_mmode; break; case 2: *(short*)mm_addr = (short)value; mm_addr += mm_mmode; break; case 4: *(long*)mm_addr=value; mm_addr += mm_mmode; break; } } } } } else /* cr pressed */ { mm_addr+=mm_mmode; } } } /*----------------------------------------------------------------------- ** "mv" Keyboard command to move a block of memory ** "mv xxxx yyyy zzzz" ** xxxx = source address ** yyyy = byte count ** zzzz = destination address **------------------------------------------------------------------------ */ void movemem(void) { static long *x = 0; static long y = 256; static long *z = 0; sscanf(linebuffer,"%lx %lx %lx",&x,&y,&z); for ( ; y > 0 ; y-=4) { *z++ = *x++; } } void compare_memory(void) { static long *x = 0; static long y = 256; static long *z = 0; sscanf(linebuffer,"%lx %lx %lx",&x,&y,&z); for ( ; y > 0 ; y-=4) { if (*z++ != *x++) tprintf("\ndifferent at %lu",x); } } unsigned long getulong(unsigned char *pwd) { unsigned long t; /* Return the ulong (32-bits) which is pointed to by pwd */ t = *pwd++; t <<= 8; t += *pwd++; t <<= 8; t += *pwd++; t <<= 8; t += *pwd; return(t); } /*----------------------------------------------------------------------- ** Here to Clear local statistics. *-----------------------------------------------------------------------*/ void resetQ(Queue *qdptr) { qdptr->maxcount=0; qdptr->overflowcount=0; qdptr->trafficcount=0; } void clearstats(void) { resetQ(&toTPQtab); resetQ(&frTPQtab); resetQ(&toITQtab); resetQ(&toAPQtab); resetQ(&frAPQtab); resetQ(&toACQtab); resetQ(&frACQtab); resetQ(&toTCPQtab); resetQ(&frTCPQtab); initTaskTable(tp_tptr); } /*----------------------------------------------------------------------- ** 'mt' - Test Port Memory Test command on Inertial Solutions MC68332 card. *------------------------------------------------------------------------ ; Keyboard ESC halts testing and reboots. ; "." progress chatacters are output every 96 passes.. If error, ; the red LED turns on and exits to CPU32bug where registers identify ; the failed address and pass count. Push button Reset restarts the system. ; Bytes, Words, and Long patterns are tested for Address and read/write errors. ; ; No RAM is used, only ROM and registers. Once launched, RAM is wiped out. ; Push the reset button or type ESC at the keyboard to reboot. ; *-----------------------------------------------------------------------*/ void mtest (void) { tprintf("\nTest will wipe RAM. ESC now to exit, or ESC during test to reboot"); tprintf("\nOn error: Red LED is on. Registers are displayed. Reset to restart\n"); tprintf("\nA2 = Address"); tprintf("\nD1 = Read"); tprintf("\nD2 = Written"); tprintf("\nD4 = Writing"); tprintf("\nA3 = Pass Count (x 96)\n"); tprintf("\nESC to exit, or CR"); inwait(); /* Any character will start*/ // MTEST assembly language routine } /*----------------------------------------------------------------------- ** Display the interrupt vector table of MC68332. Unimplimented *-----------------------------------------------------------------------*/ #if 0 // At Ansi C conversion, the linker allocates VECBR0 memory space at zero (not okay). // Hence, this is removed for the time being. Possibly asm functions can be used later. extern (*VECBR0[256])(); //#define vtend 128 void print_iv (void) { unsigned short i=0; /* Interrupt # (byte index) */ unsigned short j=0; tcrlf(); wrhex1 ((BYTE) i, TRUE); /* Display Interrupt index */ for (i = 0; i < vtend; i++) { tspace(); wrhex4 ((ULONG)VECBR0[i], TRUE); /* Display address's */ j++; if (j > 7 ){ j = 0; tcrlf(); if (i < (vtend-1)) wrhex1 ( (BYTE) (i+1), TRUE ); /* Display Interrupt index */ } } } #endif /*********************************************************************** ** Display a set of variables continuously. ** Call with scantable = ScanItem table such as the CDisplayTable. ************************************************************************/ unsigned long scanend; // This address marks the scan table ending. void doscantext(char *string1, StringArray *SensorString, short index2, short index3, ScanItem *scantable, WORD spaces) { unsigned short i; hclear(); if (string1) tprintf (string1); /* Print column header if there is one */ if (SensorString) { tprintf (" %s ", SensorString[index2]); /* Appended text to column header */ tprintf (" %s ", SensorString[index3]); } for (i=0; scantable[i].numpointer != &scanend; i++) { tcrlf(); tplace(spaces); ttom (scantable[i].descriptor); sleep_while_txTPQ_not_empty(); /* sleep until queue is empty */ } } /***********************************************************************/ void doscan(ScanItem *scantable) { unsigned short i; doscantext(0, 0, 0, 0, &scantable[0], 18); /* Text output can be shared. */ FOREVER { home(); for (i=0; scantable[i].numpointer != &scanend; i++) { tcrlf(); (*scantable[i].func)(scantable[i].numpointer); sleep_while_txTPQ_not_empty(); /* sleep until queue is empty */ } } } /***********************************************************************/ unsigned long cdc_count = 0; unsigned long cdc_fifogood = 0; unsigned long cdc_fifocserr = 0; unsigned long cdc_fiforesync = 0; unsigned long cdc_buff_overflow = 0; unsigned long acu_msg_count = 0; unsigned long acu_head_err_count = 0; unsigned long acu_data_err_count = 0; unsigned long acu_scc_err_count = 0; unsigned long acu_buff_overflow = 0; unsigned long acu_tx_msg_count = 0; unsigned long acu_tx_err_count = 0; ScanItem CDisplayTable[] = { &cdc_count, "CDC Interrupt count", doulong, &cdc_fifogood, "CDC FIFO good", doulong, &cdc_fifocserr, " FIFO CS errors", doulong, &cdc_fiforesync, " FIFO resync", doulong, &cdc_buff_overflow, " buf overflow", doulong, &acu_msg_count, "ACU Good receive", doulong, &acu_head_err_count, " Header errors", doulong, &acu_data_err_count, " Data", doulong, &acu_scc_err_count, " scca", doulong, &acu_buff_overflow, " buf overflow", doulong, &acu_tx_msg_count, "ACU Msg sent", doulong, &acu_tx_err_count, " Not sent", doulong, &PriClk.Seconds, "Time Since Reset", doulong, &scanend, "", doulong }; void comdisplay(void) { doscan(&CDisplayTable[0]); // FOREVER loop in doscan() } /*----------------------------------------------------------------------- ** TPort Scanning Display of time. (Time is read and stored in a non-tport task) *-----------------------------------------------------------------------*/ extern Time HighResClk; Time high_res_timer_resolution, pri_timer_resolution; unsigned long timer_period_nsx100; double timer_period; unsigned long timer_clock_speed; unsigned long cpu_clock_speed; unsigned long high_res_tic_speed, pri_tic_speed; ScanItem TimeDisplayTable[] = { &HighResClk.Seconds, "HighResClk Seconds Since Reset", doulong, &HighResClk.Fraction, "HighResClk Fraction Sec", doulong, &high_res_timer_resolution.Seconds, "HighResClk Resolution Seconds", doulong, &high_res_timer_resolution.Fraction,"HighResClk Resolution Fraction", doulong, &high_res_tic_speed, "HighRes Tic/sec = 0xffffffff / Res Frac", doulong, &pri_timer_resolution.Seconds, "PriClk Resolution Seconds", doulong, &pri_timer_resolution.Fraction, "PriClk Resolution Fraction", doulong, &pri_tic_speed, "PriClk Tic/sec = 0xffffffff / Res Frac", doulong, // &PriClk.Seconds, "HighResClk Seconds Since Reset", doulong, // &PriClk.Fraction, "HighResClk Fraction Sec", doulong, // &timer_period_nsx100, "Timer period xx.xx ns", doulong, // &timer_clock_speed, "Timer Clock Hz", doulong, &scanend, "", doulong }; void tptime(void) { //scanning display of time Error result; result = GetClockResolution(HIGHRESTIMER, &high_res_timer_resolution); if (result) { tp_tptr->API_error_count++; //count GetClockTime() error error_result_GetClockTime++; } high_res_tic_speed = 0xFFFFFFFF / high_res_timer_resolution.Fraction; result = GetClockResolution(PrimaryClock, &pri_timer_resolution); if (result) { tp_tptr->API_error_count++; //count GetClockTime() error error_result_GetClockTime++; } pri_tic_speed = 0xFFFFFFFF / pri_timer_resolution.Fraction; // timer_period = (1.0/(double)timer_resolution.Fraction); // timer_period_nsx100 = (unsigned long)(timer_period *100000000000.0); // timer_clock_speed = tic_speed * // cpu_clock_speed = high_res_timer_resolution.Fraction * 8; // 8 is HW timer prescaller doscan(&TimeDisplayTable[0]); // FOREVER loop in doscan() } void time_stamp(BOOL tptask) // Time stamp for use by TPort OR non-tport task. { Error result; // local variables having the same name as globals. Time priclk; // Structure: time_t Seconds (typedef long time_t; ** PLUM HALL wants this to be signed **) // UINT4 Fractions result = GetClockTime(HIGHRESTIMER, &priclk); if (result != 0) // Error reading clock error_result_GetClockTime++; if (tptask) tprintf(" %u.%u", priclk.Seconds, (unsigned long)priclk.Fraction); //Contains embedded Yield(). else xprintf(" %u.%u", priclk.Seconds, (unsigned long)priclk.Fraction); //No embedded Yield() of non-test port task. } void ttag(void) //Test Port command { docrlf(TRUE); time_stamp(TRUE); } /*----------------------------------------------------------------------* ** Test Port command "fd" (file dump) ** Dump data from the queue "toITQbuf" [TPITQSIZE] out the serial port. ** Do so cleanly without any tprintf messages cluttering the output. ** iprintf() has written formated characters to the file buffer where they wait. **----------------------------------------------------------------------*/ void file_dump(void) { Queue *Qptr; Qptr = &toITQtab; hclear(); //home the cursor and clear the screen tprintf ("\nFile size %d chars", Qptr->count); tprintf("\nUse the mouse to create a log file on Tera Terminal: File > Log > FileName.txt"); tprintf("\n\nAfter clicking 'Save', hit the 'Return' key to start file transfer, ESC to abort, Close log file after."); getline(linebuffer); //Wait for CR key stroke to start. ESC to exit. sio_dump_on(); //fast serial output is started from work list in main task, run till empty. file_dump_flag = TRUE; //flag to test port ESC restart. FOREVER { tpsleep(); //ESC halts file dump. Manually clear queue with "clr" command. if (Qptr->count == 0) //if Queue empty { sio_dump_off(); //Repoint serial output to come from the test port queue, not file buffer. file_dump_flag = FALSE; //flag to tport() ESC restart. return; //Exit when empty or on keyboard ESC char detected elsewhere. } } } /*----------------------------------------------------------------------* ** Test Port command "ifil" Put test char's into the file dump queue. **----------------------------------------------------------------------*/ void fill_iQueue(void){ iprintf("\nABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 abcdefghijklmnopqrstuvwxyz"); } /*----------------------------------------------------------------------* ** Test Port command "uart1" Watch and display uart1 input. ** It works! 6-15-2011 NH #59,0,1 #52,0000.00,N,00000.00,E,0,0 #61,0 #76,0000.0000,N,00000.0000,E,+00000.0,M,0,00,00.0,000.00,000.0 #75,000.00,000.0 #80,0 #64,1 #51,01012005,124056 #55,0,47 #78,1,3,0,,,,,, #81,1,0,+00 #66,00000-000,00000-0000,3.0 ,3.0,302002035320 #71,1 #57,0 #73,0,+ #74,0,0,00000000,000000.0000000,00000000,0 #53,+00000,M #60,3,7 #52,0000.00,N,00000.00,E,0,0 #65,0,1,0 #76,0000.0000,N,00000.0000,E,+00000.0,M,0,00,00.0,000.00,000.0 #75,000.0#70,0 #56,-00000 #72,FF #69,15,0,S,0,22,0,S,0,24,0,S,0,11,0,S,0,20,0,S,0,17,0,S,0,21,0,S,0,02,0,S,0,10,0,S,0,07,0,S,0,04,0,S,0,31,0,S,0,2 #53,+00000,M #59,0,1 **----------------------------------------------------------------------*/ extern BOOL write_uart(IODevice IOD, char* tx_char); extern BOOL read_uart (IODevice IOD, char* rx_char); extern IODevice SerialIOD1; void watch_uart1(void) { Value val; tprintf("\nWatching uart1 input\n"); FOREVER { //Device Read Interfers-contends with other FIFO readers! while ( Success != ReadIODeviceRegister(SerialIOD1, IODEV_SERIAL_DATA, &val) ) { tpsleep(); //10 msec tested good. 1 msec tpsleep also good. 32 byte input HW FIFO. } // while not successful xputc((char)val); } } /*-------------------------------------------------------------------------------------------------* ** TM-4 GPS messages are lines of ASCII characters, each line starting with # and ending with CRLF. ** The format of GPS messages is: ** #NN,XXXXX,XXXX,XX,XXXXXXXX,XXXCRLF ** where: NN is the message number ** XXXXX designates various data fields ** CRLF is a carriage return followed by a line feed (0x0D, 0x09) ** MESSAGE #51 – DATE AND TIME ** #51,MMDDYYYY,HHMMSSCRLF ** where: MMDDYYYY is UTC month, day, and year ** HHMMSS is UTC hours, minutes and seconds ** MESSAGE #52 – POSITION ** #52,WWWW.WW,X,YYYYY.YY,Z,A,NCRLF ** where: W = latitude in DDMM.MM ** X = hemisphere N or S ** Y = longitude in DDDMM.MM ** Z = hemisphere E or W ** A = GPS availability (0 = not available, 1 = available) ** N = number of satellites used (0-9, A[10], B[11], C[12]) ** MESSAGE #53 – ALTITUDE ** #53,SXXXXX,MCRLF ** where: S = sign (+ or -) ** X = altitude (5 digits) ** M = altitude units (meters) ** MESSAGE #59 – GEOMETRIC QUALITY AND ALMANAC STATUS ** #59,X,YCRLF ** where: X = GQ (0-9) ** Y = 0 (almanac OK) ** Y = 1 (no almanac) ** Y = 2 (almanac is old) ** MESSAGE #61 – TIMING STATUS ** #61,WCRLF ** where: W = 0 (time not valid) ** W = 1 (time valid) ** MESSAGE #64 – OSCILLATOR TUNING MODE ** #64,XCRLF ** where: X = 1 for Mode 1 (oscillator warm-up) ** X = 2 for Mode 2 (course adjust) ** X = 3 for Mode 3 (course adjust standby) ** X = 4 for Mode 4 (fine adjust) ** X = 5 for Mode 5 (fine adjust hold) ** MESSAGE #65 – ALARM STATUS ** #65,X,Y,ZCRLF ** where: X = 0 (no coast condition) ** X = 1 (coast alarm condition) ** Y = 0 (antenna good) ** Y = 1 (antenna fault condition) ** Z = 0 (10 MHz frequency output good) ** Z = 1 (10 MHz frequency output fault condition) ** MESSAGE #69 – TRACKING CHANNEL STATUS ** #69,VV,W,X,Y,....VV,W,X,Y,ZCRLF ** where: VV = PRN of satellite being tracked ** W = constellation status: ** 0 = not included in current constellation ** 1 = included in current constellation ** X = tracking status: ** A = acquisition/reacquisition ** S = searching ** 0-9 = SQ ** Y = ephemeris status: ** 0 = not collected ** 1 = collected ** Z = receiver status: ** 2 = search the sky ** 3 = almanac collect ** 4 = ephemeris collect ** 5 = acquisition ** 6 = position ** NOTE: VV,W,X,Y repeats twelve times, corresponding to each of the twelve channels. ** MESSAGE #75 – SPEED OVER LAND and HEADING ** #75,SSS.SS,HHH.HCRLF ** where: SSS.SS indicates speed over land in meters/sec ** HHH.H indicates course in degrees decimal ** **-------------------------------------------------------------------------------------------------*/