/*
**
*
         Copyright (C)  P&M Software Co 1989.  All rights reserved

      THIS PROGRAM BELONGS TO P&M SOFTWARE CO.  IT IS CONSIDERED A TRADE
      SECRET AND IS NOT TO BE DIVULGED OR USED BY PARTIES WHO HAVE NOT
      RECEIVED WRITTEN AUTHORIZATION FROM THE OWNER.

*
**
*/

/*
**
*
              Uses TINY memory model.
*
**
*/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <errno.h>
#include <sys\stat.h>
#include <dos.h>
#include <conio.h>
#include <bios.h>

extern unsigned _heaplen  = 4096;
extern unsigned _stklen   = 6144;
extern int directvideo    = 0;

#define SIN_BUF_SIZE   1024
#define SOUT_BUF_SIZE  1024

typedef struct bimod_time_lim {
    char bi_hundreds;
    char bi_seconds;
    char bi_minutes;
    char bi_hours;
} BI_TIME_RECORD;

void prt_error(char *,char *);
int o_mode(int);
int skipper(char *,int);
int sfopen(char *,int);
int setup_bimodem(void);
void main(int,char *[]);

static char bipath[80];
static char bi_cfg[80];
static char bi_mode[32];
static char bi_xmit_pth[80];
static char bi_recv_pth[80];
static char bi_name[80];
static char twoway_bbs[32];
static char gws[4096];
static unsigned int ramt;
static BI_TIME_RECORD bi_time;
static unsigned int pc_com_addr[] = { 0x03f8, 0x02f8, 0x03e8, 0x02e8 };
static unsigned int pc_irq_asg[]  = {      4,      3,      4,      3 };
static int com_port;
static unsigned long baud_rate;
static unsigned char sin_buffer[SIN_BUF_SIZE];
static int sin_store_ptr;
static int sin_read_ptr;
static unsigned char sout_buffer[SOUT_BUF_SIZE];
static int sout_store_ptr;
static int sout_read_ptr;
static unsigned char turn_irq_on;
static unsigned char turn_irq_off;
static int xmit_busy;
static unsigned long irq_vector_addr;
static unsigned long hold_vector;
static int carrier_status = 1;
static unsigned int base_com_addr;
static unsigned int int_enable_reg;
static unsigned int int_enable;
static unsigned int int_ident_reg;
static unsigned int line_control_reg;
static unsigned int modem_control_reg;
static unsigned int modem_control;
static unsigned int line_status_reg;
static unsigned int modem_status_reg;
static unsigned char far *machine_id;

char read_sin_buffer()
{
    register char function;

    function = sin_buffer[sin_read_ptr];
    if (++sin_read_ptr == SIN_BUF_SIZE)
	sin_read_ptr = 0;
    return(function);
}

char *incom()
{
    static char incom_return[2];

    if (sin_store_ptr == sin_read_ptr)
	return(NULL);
    incom_return[0] = read_sin_buffer();
    incom_return[1] = 0;
    return(incom_return);
}

int isDCD()
{
    return((inportb(modem_status_reg) & 0x0080));
}

int iscarrier()
{
    register int res;

    res = 1;
    if (! isDCD()) {
        if (carrier_status)
            delay(10);
        if (! isDCD())
            res = 0;
    }
    return((carrier_status = res));
}

void store_sout_buffer(ch)
char ch;
{
    sout_buffer[sout_store_ptr++] = ch;
    if (sout_store_ptr == SOUT_BUF_SIZE)
	sout_store_ptr = 0;
    if (xmit_busy)
	return;
    disable();
    if (xmit_busy)
	goto L1soutx;
    xmit_busy = 1;
    outportb(base_com_addr,(int) sout_buffer[sout_read_ptr]);
    if (++sout_read_ptr == SOUT_BUF_SIZE)
	sout_read_ptr = 0;
L1soutx:
    enable();
}

void send_str(s)
char *s;
{
    register int i, j;

    i = 0;
    while (j = s[i++]) {
        store_sout_buffer(j);
	putch(j);
	iscarrier();
	if (! carrier_status)
	    return;
        delay(5);
    }
}

void set_int_vector(vaddr,value)
unsigned long vaddr;
unsigned long value;
{
    disable();
    *((unsigned long far *) vaddr) = value;
    enable();
}

unsigned long get_int_vector(vaddr)
unsigned long vaddr;
{
    unsigned long result;

    disable();
    result = *((unsigned long far *) vaddr);
    enable();
    return(result);
}

void reset_modem_vectors()
{
    disable();
    int_enable = inportb(int_enable_reg);
    outportb(int_enable_reg,(int_enable & 0xf0));
    outportb(0x21,(inportb(0x21) | turn_irq_off));
    enable();
    set_int_vector(irq_vector_addr,hold_vector);
}

void interrupt far async_intr_handler()
{
L1loop1:
    switch (inportb(int_ident_reg)) {
        case 1:
            goto L1iret;
        case 2:
L1loop2:
            if (sout_store_ptr == sout_read_ptr)
                xmit_busy = 0;
            else {
                outportb(base_com_addr,(int) sout_buffer[sout_read_ptr]);
                sout_read_ptr++;
                if (sout_read_ptr == SOUT_BUF_SIZE)
		    sout_read_ptr = 0;
                xmit_busy = 1;
            }
            goto L1loop1;
	case 12:
        case  4:
            sin_buffer[sin_store_ptr] = (char) inportb(base_com_addr);
            sin_store_ptr++;
            if (sin_store_ptr == SIN_BUF_SIZE)
		sin_store_ptr = 0;
            goto L1loop1;
        case 0:
            inportb(modem_status_reg);
            goto L1rsx;
        case 6:
            inportb(line_status_reg);
            break;
        default:
            inportb(modem_status_reg);
            inportb(line_status_reg);
            break;
    }
    goto L1loop1;
L1rsx:
    if (xmit_busy)
        goto L1loop1;
    goto L1loop2;
L1iret:
    outportb(0x20,0x20);
}

#define pcjr 253

void setserial(baud)
unsigned long baud;
{
    static int divs[] = {384, 192, 96, 48, 24, 12, 6, 3, 1};
    int parameter;
    int baudrate;
    int stopbits, databits;
    union REGS regs;

    baudrate = 4;
    if (baud == 300)     baudrate = 2;
    if (baud == 1200)    baudrate = 4;
    if (baud == 2400)    baudrate = 5;
    if (baud == 4800)    baudrate = 6;
    if (baud == 9600)    baudrate = 7;
    if (baud == 19200L)  baudrate = 8;
    if (baud == 38400L)  baudrate = 9;
    if (baud == 115200L) baudrate = 10;
    stopbits = 0;            /* Default to 1 stop bit */
    databits = 3;            /* Default to 8 data bits */
    parameter = (stopbits << 2) + databits;
    int_enable = inportb(int_enable_reg);
    outportb(int_enable_reg,(int_enable & 0xf0));
    if (*machine_id == pcjr) {
        parameter = parameter | ((baudrate << 5) & 0x00ff);
        regs.x.dx = com_port - 1;
        regs.x.ax = parameter;
        int86(0x14,&regs,&regs);
    }
    else  {
        outportb(line_control_reg,0x80);
        baudrate -= 2;
        outportb(int_enable_reg,((divs[baudrate] >> 8) & 0x00ff));
        outportb(base_com_addr,(divs[baudrate] & 0x00ff));
        outportb(line_control_reg,parameter);
        inportb(modem_status_reg);
	inportb(int_ident_reg);
	inportb(base_com_addr);
        inportb(line_status_reg);
    }
    outportb(int_ident_reg,0x0);
    modem_control = inportb(modem_control_reg) & 0xef;
    outportb(modem_control_reg,(modem_control | 0x0b));
    disable();
    outportb(0x21,(inportb(0x21) & turn_irq_on));
    enable();
    int_enable = inportb(int_enable_reg);
    outportb(int_enable_reg,(int_enable | 0x0f));
    delay(10);
}

void initialize_com_port()
{
    typedef int com_tab[4];
    register int cp, irq;

    cp = com_port - 1;
    machine_id = (char far *) 0xF000FFFEL;
    if (*machine_id == pcjr)
        base_com_addr = (*((com_tab *) 0x00000400))[cp];
    else
        base_com_addr = pc_com_addr[cp];
    cp = 0;
    while (base_com_addr != pc_com_addr[cp])
	cp++;
    irq = pc_irq_asg[cp];
    turn_irq_off = (0x01 << irq);
    turn_irq_on  = (turn_irq_off ^ 0xff);
    irq_vector_addr = (unsigned long) (0x20 + (4 * irq));
    sin_store_ptr =
     sin_read_ptr =
      sout_store_ptr =
       sout_read_ptr = 0;
    int_enable_reg = base_com_addr + 1;
    int_ident_reg = int_enable_reg + 1;
    line_control_reg = int_ident_reg + 1;
    modem_control_reg = line_control_reg + 1;
    line_status_reg = modem_control_reg + 1;
    modem_status_reg = line_status_reg + 1;

    hold_vector = get_int_vector(irq_vector_addr);
    set_int_vector(irq_vector_addr,(unsigned long) async_intr_handler);
    setserial(baud_rate);
}

void prt_error(fs,ns)
char *fs;
char *ns;
{
    perror("\nGTBIMOD");
    printf(fs,ns);
    sleep(2);
}

int o_mode(omd)
int omd;
{
    register int md;

    md = omd;
    if (_osmajor > 2)
	md = (md | O_DENYNONE);
    return(md);
}

int skipper(jstr,jo)
char *jstr;
int jo;
{
   while ((jo < ramt) && (jstr[jo]) && (jstr[jo] <= ' '))
       jo++;
   return(jo);
}

int sfopen(fname,omode)
char *fname;
int omode;
{
    register int handle, loop;
    char buf[80];

    loop = handle = (-1);
    while (handle < 0) {
	if ((handle = _open(fname,o_mode(omode))) < 0) {
            if ((++loop >= 10) || (errno != EACCES)) {
	        sprintf(buf,"%s%s","Unable to open: ",fname);
		prt_error("SFOPEN: %s",buf);
	        return(-1);
	    }
            sleep(1);
	}
    }
    return(handle);
}

void extract(ds,j)
char *ds;
int *j;
{
    register int k, jp;

    jp = *j;
    k  = 0;
    while ((jp < ramt) && (gws[jp] > ' '))
        ds[k++] = gws[jp++];
    *j = skipper(gws,jp);
}

int setup_bimodem()
{
    int jp, cfg, twoway, tim, result, lpth;
    char logpath[96], ws[128];

    result = 0;
    sprintf(bi_cfg,"%sBIMODEM.CFG",bipath);
    strupr(bi_cfg);
    if ((cfg = sfopen(bi_cfg,O_RDWR)) < 0)
        return(1);
    strcpy(twoway_bbs,"TWOWAY.BBS");
    if ((twoway = sfopen(twoway_bbs,O_RDWR)) < 0) {
	_close(cfg);
        return(1);
    }
    memset((void *) gws,0,sizeof(gws));
    lseek(twoway,0L,SEEK_SET);
    ramt = _read(twoway,(void *) gws,(sizeof(gws)-16));
    memset((void *) logpath,0x20,sizeof(logpath));
    lseek(twoway,0L,SEEK_SET);
    lpth = sprintf(logpath,"L %sBIMODEM.LOG\r\n",bipath);
    _write(twoway,(void *) logpath,lpth);
    _close(twoway);
    memset((void *) bi_mode,0,sizeof(bi_mode));
    memset((void *) bi_xmit_pth,0x20,sizeof(bi_xmit_pth));
    memset((void *) bi_recv_pth,0x20,sizeof(bi_recv_pth));
    jp = 0;
    extract(bi_mode,    &jp);
    extract(bi_xmit_pth,&jp);
    extract(bi_recv_pth,&jp);
    bi_mode[4] = 0;
    tim = 120;
    if (! strcmp(bi_mode,"TERM")) {
        result = 2;
	tim = 720;
    }
    if (! strcmp(bi_mode,"MAIL"))
	result = 4;
    if (! strcmp(bi_mode,"HOST")) {
        result = 3;
        tim = atoi(&bi_mode[5]);
    }
    printf("\nMODE = %s",bi_mode);
    memmove((void *) ws,(void *) bi_xmit_pth,50);
    ws[50] = 0;
    printf("\nXMIT = %s",ws);
    memmove((void *) ws,(void *) bi_recv_pth,50);
    ws[50] = 0;
    printf("\nRECV = %s\n",ws);
    printf("\nTIME = %d",tim);
    bi_time.bi_hundreds = 0;
    bi_time.bi_seconds  = 0;
    bi_time.bi_minutes  = (char) (tim % 60);
    bi_time.bi_hours    = (char) (tim / 60);
    lseek(cfg,0L,SEEK_SET);
    _write(cfg,(void *) &bi_time,4);
    lseek(cfg,37L,SEEK_SET);
    _read(cfg,(void *) ws,16);
    ws[0] |= 0x60;
    lseek(cfg,37L,SEEK_SET);
    _write(cfg,(void *) ws,16);
    lseek(cfg,41L,SEEK_SET);
    _write(cfg,(void *) bi_xmit_pth,80);
    _write(cfg,(void *) bi_recv_pth,80);
    logpath[lpth--] = 0x20;
    logpath[lpth--] = 0x20;
    logpath[lpth]   = 0x20;
    _write(cfg,(void *) &logpath[2],80);
    _close(cfg);
    sprintf(ws,"DEL %sBIMODEM.LOG",bipath);
    system(ws);

    sprintf(bi_cfg,"%sBIMODEM.PTH",bipath);
    strupr(bi_cfg);
    if (result == 3) {
        sprintf(ws,"DEL %s",bi_cfg);
        system(ws);
    }
    if (result == 4) {
	memset((void *) bi_recv_pth,0x20,sizeof(bi_recv_pth));
        if ((cfg = _creat(bi_cfg,0)) < 0)
            return(1);
        while (jp < ramt) {
            memset((void *) bi_name,0x20,sizeof(bi_name));
	    extract(bi_name,&jp);
	    _write(cfg,(void *) "U       ",8);
	    _write(cfg,(void *) bi_name,80);
	    _write(cfg,(void *) bi_recv_pth,80);
	    _write(cfg,(void *) bi_recv_pth,80);
	}
	_close(cfg);
    }
    return(result);
}

int keytest()
{
    return(bioskey(1));
}

int keyread()
{
    return((bioskey(0) & 0x00FF));
}

void main(argc,argv)
int argc;
char *argv[];
{
    register int j, loop;
    unsigned int lcnt, ticks;
    int rescode, pcnt, pa;
    char *p;
    char prior;

    directvideo = 0;
    rescode = 1;
    delay(0);
    if (argc < 4) {
        printf(  "\nGTBIMOD usage: gtbimod port baud bipath\\");
        printf("\n\n    Where: 'port' ...... x:yyyy:z");
	printf(  "\n                                 x = COM port #");
	printf(  "\n                              yyyy = COM port addr");
	printf(  "\n                                 z = IRQ #");
	printf(  "\n           'baud' ...... is the current baud rate");
	printf(  "\n           'bipath\\' ... points to a directory containing these files:");
	printf("\n\n                 1.  BIMODEM.CFG");
	printf(  "\n                 2.  BIMODEM.LOG");
	printf(  "\n                 3.  BIMODEM.PTH");
	printf(  "\n");
        exit(1);
    }
    printf("PORT = %s\nBAUD = %s\nPATH = %s\n",argv[1],argv[2],argv[3]);
    com_port = argv[1][0] - '0';
    j = com_port - 1;
    if (argv[1][1] == ':') {
        sscanf(&argv[1][2],"%x",&pa);
        pc_com_addr[j] = pa;
	if (argv[1][6] == ':')
            pc_irq_asg[j] = argv[1][7] - '0';
    }
    baud_rate = atol(argv[2]);
    initialize_com_port();
    strcpy(bipath,argv[3]);
    j = strlen(bipath);
    if (bipath[(j - 1)] != '\\') {
        bipath[j++] = '\\';
        bipath[j] = 0;
    }
    strupr(bipath);
    iscarrier();
    if (carrier_status) {
        if ((rescode = setup_bimodem()) == 3) {
	    loop = iscarrier();
	    if (! loop)
		rescode = 1;
	    send_str("\r\n\nThe HOST is ready.   Press ENTER when you are ready: ");
	    pcnt =
	     prior =
	      lcnt =
	       ticks = 0;
            while (loop) {
		if (++lcnt > 100) {
		    lcnt = 0;
		    if (++ticks > 55000U) {
			send_str("\r\n\nTimeout...\r\n\n");
			loop = 0;
			rescode = 1;
		    }
		    delay(10);
		}
                p = incom();
		if (p != NULL) {
		    store_sout_buffer(p[0]);
		    putch(p[0]);
		    if (p[0] == 13) {
			send_str("\n\n");
			loop = 0;
		    }
		    else {
			if (((p[0] == ' ') && (prior == 8)) || ((p[0] == 8) && (prior == ' '))) {
		            if (pcnt++ > 1)
				loop = 0;
			}
		    }
		    prior = p[0];
		}
		else {
		    if (keytest()) {
			if ((keyread()) == 13) {
			    send_str("\n\n");
			    loop = 0;
			}
		    }
		}
		iscarrier();
		if (! carrier_status) {
		    loop = 0;
		    rescode = 1;
		}
	    }
	}
    }
    reset_modem_vectors();
    exit(rescode);
}

/*
**
*
         Copyright (C)  P&M Software Co 1989.  All rights reserved

      THIS PROGRAM BELONGS TO P&M SOFTWARE CO.  IT IS CONSIDERED A TRADE
      SECRET AND IS NOT TO BE DIVULGED OR USED BY PARTIES WHO HAVE NOT
      RECEIVED WRITTEN AUTHORIZATION FROM THE OWNER.

*
**
*/
