/* * RTTY.C was developed as a method of controlling a * HAL Communications ST-6 teletype Terminal Unit (TU) which * was modified for RS-232. It is a very simple terminal * program that controls DTR for transmit/receive and RTS * for morse code ID. It has a morse code generator built * in. It is written by AA6ED in Borland C/C++ version 4.0 * and also sports a serial communitations driver that * control up to eight COM ports IRQ 1-15 all at the same * time. To make this work for your computer, you will have * to find the parameter for setting the COM port and * rebuild the application. * * Permission granted for non-commercial copying and use, * provided this notice is retained. Copyright 1999 * William H. Bytheway AA6ED, All Rights Reserved. */ #include #include #include #include #include #include "rtty.h" /* nasty globals */ #define COMPORT COM5 /* This is where you define the COM port */ #define MYCALL "de aa6ed" /* Put your callsign here */ #define TRUE 1 #define FALSE 0 int transmitting = FALSE; /* check if transmitting */ int repeatlock = FALSE; /* used for character echo */ int num_interrupts = 0; /* Define the communications port absolute numbers */ #define COM1 0 #define COM2 1 #define COM3 2 #define COM4 3 #define COM5 4 #define COM6 5 #define COM7 6 #define COM8 7 #define buff_size 0xFF /* Define the interrupt mask structure for every IRQ interrupt */ struct { unsigned int port_address; unsigned char int_id; unsigned char base_is_set; unsigned char datardy; unsigned char ringbuf[buff_size+1]; unsigned int readptr; unsigned int writptr; } COM[8] = { { 0x3F8, 4, 0, 0, 0, 0, 0 }, /* COM 1 */ { 0x2F8, 3, 0, 0, 0, 0, 0 }, /* COM 2 */ { 0x3E8, 10, 0, 0, 0, 0, 0 }, /* COM 3 */ { 0x3E0, 11, 0, 0, 0, 0, 0 }, /* COM 4 */ { 0x2F0, 12, 0, 0, 0, 0, 0 }, /* COM 5 */ { 0x2E8, 15, 0, 0, 0, 0, 0 }, /* COM 6 */ { 0x2E0, 0, 0, 0, 0, 0, 0 }, /* COM 7 */ { 0x260, 0, 0, 0, 0, 0, 0 }}; /* COM 8 */ /* Define the IRQ interrupt addresses and masks */ struct { unsigned char int_id; unsigned char PIC1_mask; unsigned char PIC2_mask; unsigned char old_PIC1_mask; unsigned char old_PIC2_mask; } IRQ[16] = { { 0x08, 0xFE, 0xFF, 0, 0}, /* IRQ00 tick counter */ { 0x09, 0xFD, 0xFF, 0, 0}, /* IRQ01 Keyboard */ { 0x0A, 0xFB, 0xFF, 0, 0}, /* IRQ02 PIC2 */ { 0x0B, 0xF7, 0xFF, 0, 0}, /* IRQ03 COM2 */ { 0x0C, 0xEF, 0xFF, 0, 0}, /* IRQ04 COM1 */ { 0x0D, 0xDF, 0xFF, 0, 0}, /* IRQ05 Mouse */ { 0x0E, 0xBF, 0xFF, 0, 0}, /* IRQ06 Floppy Drive */ { 0x0F, 0x7F, 0xFF, 0, 0}, /* IRQ07 Printer */ { 0x70, 0xFB, 0xFE, 0, 0}, /* IRQ08 Clock */ { 0x71, 0xFB, 0xFD, 0, 0}, /* IRQ09 Redirect IRQ2*/ { 0x72, 0xFB, 0xFB, 0, 0}, /* IRQ10 COM3 */ { 0x73, 0xFB, 0xF7, 0, 0}, /* IRQ11 COM4 */ { 0x74, 0xFB, 0xEF, 0, 0}, /* IRQ12 COM5 */ { 0x75, 0xFB, 0xDF, 0, 0}, /* IRQ13 System Int */ { 0x76, 0xFB, 0xBF, 0, 0}, /* IRQ14 Fixed Disk */ { 0x77, 0xFB, 0x7F, 0, 0}}; /* IRQ15 COM6 */ /* Define address adders for the various Async card registers */ #define RBR 0x00 /* Transmitter Holding Register */ #define IER 0x01 /* Interrupt Enable Register */ #define IIR 0x02 /* Interrupt Identification Register */ #define LCR 0x03 /* Line Control Register */ #define MCR 0x04 /* Modem Control Register */ #define LSR 0x05 /* Line Status Register */ #define MSR 0x06 /* Modem Status Register */ #define DLL 0x00 /* Divisor Latch Least Significant */ #define DLM 0x01 /* Divisor Latch Most Significant */ /* Define port addresses for the 8259 interrupt controller */ #define PICCMD1 0x20 /* Primary Controller */ #define PICMSK1 0x21 #define PICCMD2 0xA0 /* Secondary Controller */ #define PICMSK2 0xA1 #define PICEOI 0x20 /* End of interrupt */ /* Define UART byte settings */ #define pSpace 0x38 #define pOdd 0x08 #define pMark 0x28 #define pEven 0x18 #define pNone 0x00 /* Define UART number of data bits */ #define d5 0x00 #define d6 0x01 #define d7 0x02 #define d8 0x03 /* define UART number of stop bits */ #define s1 0x00 #define s2 0x04 #define true 1 #define false 0 /* Store the pointers to the interrupt address */ void interrupt (*Com1)(); void interrupt (*Com2)(); void interrupt (*Com3)(); void interrupt (*Com4)(); void interrupt (*Com5)(); void interrupt (*Com6)(); void interrupt (*Com7)(); void interrupt (*Com8)(); /* Async Interrupt routine */ void Asynch_COM(int irq_id) { int baseid; num_interrupts++; for (baseid = 0; baseid <= 7; baseid++) { if ((COM[baseid].int_id == irq_id) && COM[baseid].base_is_set) { int ComBase = COM[baseid].port_address; if (inportb(ComBase+IIR) == 0x04) { COM[baseid].ringbuf[COM[baseid].writptr] = inportb(ComBase + RBR); /* repeat data to external hardware*/ if (!repeatlock) outportb(ComBase+RBR, COM[baseid].ringbuf[COM[baseid].writptr]); COM[baseid].writptr = (COM[baseid].writptr + 1) & buff_size; COM[baseid].datardy = true; } if (irq_id > 7) outportb(PICCMD2, PICEOI); outportb(PICCMD1,PICEOI); } /* test for matching interrupt and base is set */ } /* loop through 8 COM ports and test for baseid */ } /* end of the interrupt at this point and return */ /* Pass the correct interrupt to the Async_COM(interrupt id) */ void interrupt IRQ0_Interrupt() { Asynch_COM(0); } void interrupt IRQ1_Interrupt() { Asynch_COM(1); } void interrupt IRQ2_Interrupt() { Asynch_COM(2); } void interrupt IRQ3_Interrupt() { Asynch_COM(3); } void interrupt IRQ4_Interrupt() { Asynch_COM(4); } void interrupt IRQ5_Interrupt() { Asynch_COM(5); } void interrupt IRQ6_Interrupt() { Asynch_COM(6); } void interrupt IRQ7_Interrupt() { Asynch_COM(7); } void interrupt IRQ8_Interrupt() { Asynch_COM(8); } void interrupt IRQ9_Interrupt() { Asynch_COM(9); } void interrupt IRQ10_Interrupt() { Asynch_COM(10); } void interrupt IRQ11_Interrupt() { Asynch_COM(11); } void interrupt IRQ12_Interrupt() { Asynch_COM(12); } void interrupt IRQ13_Interrupt() { Asynch_COM(13); } void interrupt IRQ14_Interrupt() { Asynch_COM(14); } void interrupt IRQ15_Interrupt() { Asynch_COM(15); } /* another global */ int ComBase; /* Open the COM port */ void OpenCom(int ComPort,int baud,int parity,int databits,int stopbits) { int rate; int LCRreg; int interrupt_id; int interrupt_no; COM[ComPort].base_is_set = true; interrupt_no = COM[ComPort].int_id; interrupt_id = IRQ[interrupt_no].int_id; switch (ComPort) { case 0: Com1 = getvect(interrupt_id); break; case 1: Com2 = getvect(interrupt_id); break; case 2: Com3 = getvect(interrupt_id); break; case 3: Com4 = getvect(interrupt_id); break; case 4: Com5 = getvect(interrupt_id); break; case 5: Com6 = getvect(interrupt_id); break; case 6: Com7 = getvect(interrupt_id); break; case 7: Com8 = getvect(interrupt_id); break; } disable(); switch (interrupt_no) { case 0: setvect(interrupt_id,IRQ0_Interrupt); break; case 1: setvect(interrupt_id,IRQ1_Interrupt); break; case 2: setvect(interrupt_id,IRQ2_Interrupt); break; case 3: setvect(interrupt_id,IRQ3_Interrupt); break; case 4: setvect(interrupt_id,IRQ4_Interrupt); break; case 5: setvect(interrupt_id,IRQ5_Interrupt); break; case 6: setvect(interrupt_id,IRQ6_Interrupt); break; case 7: setvect(interrupt_id,IRQ7_Interrupt); break; case 8: setvect(interrupt_id,IRQ8_Interrupt); break; case 9: setvect(interrupt_id,IRQ9_Interrupt); break; case 10: setvect(interrupt_id,IRQ10_Interrupt); break; case 11: setvect(interrupt_id,IRQ11_Interrupt); break; case 12: setvect(interrupt_id,IRQ12_Interrupt); break; case 13: setvect(interrupt_id,IRQ13_Interrupt); break; case 14: setvect(interrupt_id,IRQ14_Interrupt); break; case 15: setvect(interrupt_id,IRQ15_Interrupt); break; } IRQ[interrupt_no].old_PIC2_mask = inportb(PICMSK2); IRQ[interrupt_no].old_PIC1_mask = inportb(PICMSK1); outportb(PICMSK2, (IRQ[interrupt_no].old_PIC2_mask & IRQ[interrupt_no].PIC2_mask)); outportb(PICMSK1, (IRQ[interrupt_no].old_PIC1_mask & IRQ[interrupt_no].PIC1_mask)); enable(); outportb(PICCMD2,PICEOI); outportb(PICCMD1,PICEOI); ComBase = COM[ComPort].port_address; outportb(IER+ComBase, 0x01); outportb(MCR+ComBase, 0x08); LCRreg = 0x80; LCRreg = LCRreg | parity; LCRreg = LCRreg | databits; LCRreg = LCRreg | stopbits; outportb(LCR+ComBase, LCRreg); rate = 115200L / baud; /* Load the two bytes of the register */ outportb(DLM+ComBase, (char)(rate>>8 & 0xFF)); /* Hi byte */ outportb(DLL+ComBase, (char)(rate & 0xFF)); /* Low byte */ outportb(LCR+ComBase, LCRreg & 0x7F); /* final flush to clear both interrupt controllers */ Asynch_COM(COM[COMPORT].int_id); } /*------------ Close any initialized COM -----------------------*/ void CloseCom(int ComPort) { int interrupt_no, interrupt_id, ComBase; ComBase = COM[ComPort].port_address; COM[ComPort].base_is_set = false; interrupt_no = COM[ComPort].int_id; interrupt_id = IRQ[interrupt_no].int_id; outport(PICMSK2, (IRQ[interrupt_no].old_PIC2_mask)); outport(PICMSK1, (IRQ[interrupt_no].old_PIC1_mask)); switch (ComPort) { case 0: setvect(interrupt_id,Com1); break; case 1: setvect(interrupt_id,Com2); break; case 2: setvect(interrupt_id,Com3); break; case 3: setvect(interrupt_id,Com4); break; case 4: setvect(interrupt_id,Com5); break; case 5: setvect(interrupt_id,Com6); break; case 6: setvect(interrupt_id,Com7); break; case 7: setvect(interrupt_id,Com8); break; } outportb(IER+ComBase, 0x00); outportb(MCR+ComBase, 0x00); } /* Checks the datardy status for a given receive port and gets the char */ /* It returns a -1 if there isn't anything, or the character if there is */ int recvchar(int ComPort) { int ch = -1; if (COM[ComPort].datardy) { ch = COM[ComPort].ringbuf[COM[ComPort].readptr]; COM[ComPort].readptr = (COM[ComPort].readptr + 1) & buff_size; if (COM[ComPort].readptr == COM[ComPort].writptr) COM[ComPort].datardy = false; } return(ch); } /* Transmits a character to the appropriate communications port */ int xmitchar(int ComPort, int ch) { int LSRreg,i=-1; do { LSRreg = inportb(LSR+COM[ComPort].port_address) & 0x20; i++; } while (LSRreg == 0); outportb(RBR+COM[ComPort].port_address,ch); return(i); } /* Returns a -1 if there is no DCD connect, a +1 if there is */ int DCDconnect(int ComPort) { int ComBase; int i = -1; ComBase = COM[ComPort].port_address; /* Set ComBase Address */ if ((inportb(ComBase + MSR) & 0x80) == 0x80) i = 1; return (i); } /* Resets the input buffer on any given comport */ void purgeline(int ComPort) { COM[ComPort].readptr = 0; COM[ComPort].writptr = 0; COM[ComPort].datardy = false; } /* finis rs-232 utility */ /* ----------------------------- MAIN PROGRAM ------------------ */ void send_baudot(int ch1) { int indx = 0; char c; indx = (int) ch1; if (indx > 96) indx -= 32; /* lower case becomes upper case */ state= table3[indx].state; /* get BAUDOT case */ c= table3[indx].baudot; /* get Baudot character */ if (c != UU) /* untranslatable! */ { switch (c) { case BCR: xmitchar(COMPORT,BCR);/* baudot return */ c = BLF; /* let following do line feed */ state = LTRS; cprintf("\n"); break; case FIGS: state= FIGS; break; /* FIGS c*/ case LTRS: /* letters */ case BLF: c = LTRS; /* baudot line feed replace LTRS */ case BSP: state = LTRS; break; /* baudot space*/ default: break; } if (state != last_state) { if (state==LTRS) { xmitchar(COMPORT,LTRS); last_state = LTRS; } else if (state==FIGS) { xmitchar(COMPORT,FIGS); last_state = FIGS; } } xmitchar(COMPORT,c); cprintf("%c",ch1); } else cprintf("%c",'~'); return; } /* morse code utility for sending ID */ void morse(char * input) { int i; int j; int k; char in; int l; int timex; for (i=0; i 3) nndex = 0; /* circular storage */ if ((nn[0] == 'N') && (nn[1] == 'N') && (nn[2] == 'N') && (nn[3] == 'N')) { nn[0] = '\n'; nn[1] = '\n'; nn[2] = '\n'; nn[3] = '\n'; if (transmitting) { morse(MYCALL); outportb(MCR+ComBase, 0x08); cprintf("\n\r"); textcolor(YELLOW); textbackground(GREEN); cprintf(""); textcolor(BLUE); textbackground(LIGHTGRAY ); cprintf("\n\r"); transmitting = FALSE; repeatlock = FALSE; } delay(3000); purgeline(COMPORT); } return; } /* keeps the Model 28 cariage from slamming into the stops */ void autoreturn(char ch) { if (((wherex() > 66) && (ch == ' ')) || (wherex() > 75)) { if (transmitting) send_baudot('\r'); else cprintf("\n\r"); } return; } /* ---------------------------------- MAIN -----------------------------*/ int main() { char c = 0; char ch = 0; int ch1 = 0; char ch2 = 0; int c_hi; int c_lo; int counter = 0; char countmask = 0; int statex; textmode(C4350); window(1,1,80,2); clrscr(); textcolor(YELLOW); textbackground(GREEN); cprintf(" PF1 "); textcolor(YELLOW); textbackground(RED); cprintf(" PF2 "); textcolor(YELLOW); textbackground(BROWN); cprintf(" PF3 [CWID] "); textbackground(BLACK); cprintf(" "); textbackground(BLUE); textcolor(YELLOW); cprintf(" PF5 [MARK] " ); textbackground(GREEN); cprintf(" PF6 [SPACE] "); textbackground(BROWN); cprintf(" PF7 [NoSound] "); textbackground(BLACK); /* SETUP THE MAIN RECEIVE WINDOW */ window(1,2,80,50); textcolor(BLUE); textbackground(LIGHTGRAY); clrscr(); setcbrk(0); OpenCom(COMPORT,45,pNone,d5,s2); do { /* Receive BAUDOT character */ if (COM[COMPORT].datardy) { ch = recvchar(COMPORT) & 0x7F; switch (ch) { case FIGS: statex= 32; /* subsequent characters from FIGS case */ state = FIGS; break; case LTRS: /* subsequent characters from letters */ case BCR : /* case shift down */ case BLF : case BSP : statex= 0; /* case shift down */ state= LTRS; break; default: break; } ch= table1[ch + statex];/* lookup Baudot character, */ switch (ch) { case 0 : break; case UU : break; case 10 : break; case 7 : cprintf("~"); break; case 13 : cprintf("\n\r"); break; /* force cr/lf */ default : cprintf("%c",ch); autoreturn(ch); /* fource linefeed */ break; } nnnn(ch); /* lock receive if nnnn received */ } if (bioskey(1) !=0) /* Keyboard */ { ch1 = bioskey(0); c_lo = ch1 & 0x7F; c_hi = (ch1>>8) & 0x7F; switch (c_lo) { case 0: /* function key */ switch (c_hi) { /*F1*/ case 59: outportb(MCR+ComBase, 0x08); /* receive */ if (transmitting) { cprintf("\n\r"); textcolor(YELLOW); textbackground(GREEN); cprintf(""); repeatlock = FALSE; delay(3000); purgeline(COMPORT); repeatlock = FALSE; textcolor(BLUE); textbackground(LIGHTGRAY ); cprintf(" \n\r"); } transmitting = FALSE; break; /*F2*/ case 60: outportb(MCR+ComBase, 0x09); /* transmit */ if (!transmitting) { cprintf("\n\r"); textcolor(YELLOW); textbackground(RED); cprintf(""); textcolor(RED); textbackground(LIGHTGRAY ); cprintf("\n\r"); repeatlock = TRUE; } transmitting = true; break; /*F3*/ case 61: if (transmitting) morse(MYCALL); break; /*F4*/ case 62: /*F5*/ case 63:nosound(); sound(2125); cprintf("[MARK=2215 Hz]"); break; /*F6*/ case 64: nosound(); sound(2295); cprintf("[SPACE=2295 Hz]"); break; /*F7*/ case 65: nosound(); cprintf("[NoSound]"); break; /*F8*/ case 66: break; /*F9*/ case 67: repeatlock = TRUE; cprintf(""); break; /*F10*/ case 68: repeatlock = FALSE; cprintf(""); break; default: break;/* ignore rest */ } break; case 18: outportb(MCR+ComBase, 0x08); transmitting = FALSE; textcolor(BLUE); break; /*c-R receive*/ case 20: outportb(MCR+ComBase, 0x09); transmitting = TRUE; textcolor(RED); break; /*c-T transmit*/ case 9: if (transmitting) morse(MYCALL); break; /*c-I CW-ID*/ case 27: CloseCom(COMPORT); exit(0); /* quit the program */ break; default: if (transmitting) { send_baudot(c_lo); counter = 0; autoreturn((char) c_lo); nnnn(c_lo); } else { sound(2295); delay(20); sound(2125); delay(20); nosound(); } } /* end outer switch */ } else if (transmitting && (counter < 30) && (countmask == 1)) { counter++; if (state == FIGS) xmitchar(COMPORT,FIGS); else xmitchar(COMPORT,LTRS); /* end check for keyboard, cut and paste, and fast typeing*/ } countmask++; } while (ch1 !=27); CloseCom(COMPORT); return(0); }