/************************************************************************/ /* EXAMPLE.C Jonathan Naylor G4KLX 13th January 1990*/ /**/ /* This program demonstrates the use of the INT 14H interface for*/ /* applications programs to access the G8BPQ node software.*/ /* The important routines have comments associated with them.*/ /* It was compiled using Microsoft C 5.10, it will probably compile*/ /* under Microsoft QuickC as well.*/ /**/ /* Any comments, queries etc about this code, should be directed to me*/ /* and not to John G8BPQ. He is busy enough supporting the Node s/ware*/ /* without answering questions about code he hasn't written.*/ /**/ /* Packet:G4KLX @ GB7DAD*/ /* Address:24 Castle View Drive*/ /*Cromford*/ /*Matlock*/ /*Derbyshire DE4 3RL*/ /* Telephone:0629 822037*/ /************************************************************************/ #include #include #include #include #include #define TRUE 1 #define FALSE 0 int Check_BPQ(int); void Raise_RTS(int); void Lower_RTS(int); void Send_Break(int); int Write_TNC(int, char *, unsigned int); void Return_To_Node(int); int Connected(char *, int); int Read_TNC(int, char *, unsigned int); void Set_TNC(int, int); void Reset_TNC(int); void cru(char *); void Print_TNC(int); void Delay(int); int main(int argc, char *argv[]) { char callsign[15]; char buffer[50]; int appl_number; int port; int i; if (argc != 3) { fprintf(stderr, "Usage: example port application_number\n"); exit(1); } /* The INT 14H port number is 1 below the commonly used port*/ /* numbers. eg COM1: = 0, COM2: = 1 etc.*/ port = atoi(argv[1]) - 1; appl_number = atoi(argv[2]); if (!Check_BPQ(port)) exit(1); Set_TNC(appl_number, port); while (!kbhit()) { if (Connected(callsign, port)) { Print_TNC(port); Send_Break(port); strcpy(buffer, "CONV\r"); Write_TNC(port, buffer, strlen(buffer)); Print_TNC(port); sprintf(buffer, "Hello %s\r", callsign); Write_TNC(port, buffer, strlen(buffer)); Print_TNC(port); strcpy(buffer, "Welcome to the G4KLX example program\r"); Write_TNC(port, buffer, strlen(buffer)); Print_TNC(port); strcpy(buffer, "Bye Bye\r"); Write_TNC(port, buffer, strlen(buffer)); Print_TNC(port); /* Returning the user to the Node immediately */ /* appears to lose the sent data, hence the delay */ Delay(3); Return_To_Node(port); Print_TNC(port); } } Reset_TNC(port); return(0); } /************************************************************************/ /* This checks for the G8BPQ Node signiature of X'AA55' in BOTH AX and*/ /* BX. COMBIOS and related programs only return it in AX. The version*/ /* number is also retrieved, this could be checked if using a newly*/ /* implemented feature of the code.*/ /************************************************************************/ int Check_BPQ(int port) { union REGS regs; regs.x.dx = port; regs.h.ah = 4; int86(0x14, ®s, ®s); if (regs.x.ax != 0xAA55 || regs.x.bx != 0xAA55) { fprintf(stderr, "example: G8BPQ node support not loaded\n"); return(FALSE); } regs.x.dx = port; regs.h.ah = 0x1F; regs.h.al = 0; int86(0x14, ®s, ®s); printf("Running G8BPQ node support version %d.%d\n", regs.h.dh, regs.h.dl); return(TRUE); } void Raise_RTS(int port) { union REGS regs; regs.x.dx = port; regs.h.ah = 6; int86(0x14, ®s, ®s); } void Lower_RTS(int port) { union REGS regs; regs.x.dx = port; regs.h.ah = 5; int86(0x14, ®s, ®s); } /************************************************************************/ /* Sending a break to the node software is the sure-fire way of getting*/ /* back to command mode from converse and transparant mode.*/ /************************************************************************/ void Send_Break(int port) { union REGS regs; regs.x.dx = port; regs.h.ah = 7; int86(0x14, ®s, ®s); } /************************************************************************/ /* This function takes the same sort of arguments as the C library*/ /* function write(). It is a non blocking function that returns when*/ /* either all of the data has been sent to the node, or when the node*/ /* cannot accept any more data.*/ /************************************************************************/ int Write_TNC(int port, char *buffer, unsigned int count) { union REGS regs; int attempts = 0; int i; for (i = 0; i < count; i++) { regs.x.dx = port; regs.h.ah = 3; do { attempts++; int86(0x14, ®s, ®s); } while (!(regs.h.ah & 0x20) && (attempts < 6)); if (attempts == 6) return(i); attempts = 0; regs.x.dx = port; regs.h.ah = 1; regs.h.al = buffer[i]; int86(0x14, ®s, ®s); } return(i); } /************************************************************************/ /* This is the gracefull way of getting rid of users, until recently the*/ /* only available option was to disconnect them from the node, this puts*/ /* them back to the node software.*/ /************************************************************************/ void Return_To_Node(int port) { union REGS regs; regs.x.dx = port; regs.h.ah = 0x1F; regs.h.al = 0x10; int86(0x14, ®s, ®s); } /************************************************************************/ /* This function serves two purposes, it indicates whether the*/ /* application is connected to a user, and if so, what their callsign*/ /* is. At present it does not return the users ssid, but it is available*/ /* if needed. The code gets the users callsign from the INT 14H and not*/ /* from parsing the "*** CONNECTED to ..." line. This piece of the code*/ /* makes use of a far pointer to the callsign within the Node software.*/ /* It then has to be converted to ASCII from AX25 format, which is*/ /* essentially shifting the characters right by one bit.*/ /************************************************************************/ int Connected(char *buffer, int port) { union REGS regs; struct SREGS segregs; unsigned char far *callsign; int ssid; int i; regs.x.dx = port; regs.h.ah = 0x1F; regs.h.al = 1; int86x(0x14, ®s, ®s, &segregs); if (regs.x.si == 0) return(FALSE); FP_SEG(callsign) = segregs.es; FP_OFF(callsign) = regs.x.si; for (i = 0; (callsign[i] >> 1) != ' ' && i < 6; i++) buffer[i] = callsign[i] >> 1; buffer[i] = '\0'; ssid = (callsign[6] >> 1) & 0x0F; return(TRUE); } /************************************************************************/ /* This routine is a non-blocking read of the port input stream.*/ /************************************************************************/ int Read_TNC(int port, char *buffer, unsigned int count) { union REGS regs; int i = 0; regs.x.dx = port; regs.h.ah = 3; int86(0x14, ®s, ®s); while ((regs.h.ah & 0x01) && (i < count)) { regs.x.dx = port; regs.h.ah = 2; int86(0x14, ®s, ®s); buffer[i++] = regs.h.al; regs.x.dx = port; regs.h.ah = 3; int86(0x14, ®s, ®s); } buffer[i] = '\0'; return(i); } void Set_TNC(int appl_number, int port) { char *command; char buffer[15]; Raise_RTS(port); Send_Break(port); command = "ECHO ON\r"; Write_TNC(port, command, strlen(command)); Print_TNC(port); sprintf(buffer, "APPL $%02X\r", appl_number); Write_TNC(port, buffer, strlen(buffer)); Print_TNC(port); command = "MONITOR OFF\r"; Write_TNC(port, command, strlen(command)); Print_TNC(port); command = "CONOK ON\r"; Write_TNC(port, command, strlen(command)); Print_TNC(port); } void Reset_TNC(int port) { char *command; Send_Break(port); command = "APPL $00\r"; Write_TNC(port, command, strlen(command)); Print_TNC(port); command = "CONOK OFF\r"; Write_TNC(port, command, strlen(command)); Print_TNC(port); command = "ECHO OFF\r"; Write_TNC(port, command, strlen(command)); Print_TNC(port); Lower_RTS(port); } /************************************************************************/ /* This function takes its name from a UNIX utility at Nottingham Uni*/ /* called Carriage Return Utility. It converts between the carriage*/ /* returns standards found on packet radio to the standards used in DOS*/ /************************************************************************/ void cru(char *string) { int i; for (i = 0; string[i] != '\0'; i++) { switch (string[i]) { case '\n': string[i] = '\r'; break; case '\r': string[i] = '\n'; break; } } } void Print_TNC(int port) { char buffer[100]; Read_TNC(port, buffer, 99); cru(buffer); printf("%s", buffer); } /************************************************************************/ /* Delay for n seconds. In a "real" piece of software, it would be wise*/ /* to give time slices to DESQview at the point. The same can also be*/ /* said of the loop in main() where it is waiting for a user.*/ /************************************************************************/ void Delay(int delay) { time_t start_time; time_t current_time; time(&start_time); do { time(¤t_time); } while ((current_time - start_time) < delay); }