/* >>:yam5.c 4/2/83 * Modem related functions for Apple * by Jeff Martin 11/11/81 * Latest update 4/2/83 * See YAMAPPLE.H for update details. */ #include "yam.h" #ifdef MMII #define MODEMSTUFF #define AUTODIAL /* * Set the baud rate of the modem (or set byteform, if baud rate invalid) */ setbaud(nbaud) unsigned nbaud; { switch(nbaud) { case 0: /* Use b0 as a kludge to set byteform... */ puts("Want to change byteform? "); if(toupper(getchar()) == 'Y') setparams(); else /* Or to reinit the ACIA (reassert DTR) */ { outp(Sport, RESETACIA); outp(Sport, (cr1 = ACIAMODE)); puts("\n"); } return OK; case 110: #ifdef KLUDGE212 setslot(MMSLOT); #endif outp(MMIICR2,(cr2 &= ~HISPEED)); break; case 300: #ifdef KLUDGE212 setslot(MMSLOT); #endif outp(MMIICR2,(cr2 |= HISPEED)); break; #ifdef KLUDGE212 case 1200: setslot(ALTSLOT); break; #endif } Baudrate = nbaud; return OK; } #ifdef KLUDGE212 /* * Set Dport and Sport to specified slot */ setslot(num) int num; { switch (num) { case ALTSLOT: Dport = ALTMDATA; Sport = ALTMSTAT; break; case MMSLOT: Dport = MDATA; Sport = MSTAT; break; } } #endif /* * Send break */ sendbrk() { outp(Sport,(cr1 | SBREAK)); wait10(20); /* Send break for 200 ms */ outp(Sport,cr1); } /* * Read the current baud rate of the modem */ readbaud() { /* Not supported by MMII */ } /* * Terminate the current connection */ bye() { /* Turn off carrier, go on hook */ #ifdef KLUDGE212 if (Sport == MSTAT) #endif outp(MMIICR2,(cr2 &= ~(HOOKOFF|TXE))); #ifdef KLUDGE212 else outp(Sport,(0x40 | ACIAMODE)); #endif } /* * Set modem character size, parity, and stop bit parameters */ setparams() { char formats[8]; int i; initb(formats,"1,5,9,13,17,21,25,29"); puts("\nThe current byteform is "); mmiiform(cr1); puts("\n"); for (i=0; i<8; ++i) { printf("\t%d --> ",i+1); mmiiform(formats[i]); } printf("\nWhich? (1..8, or 0 to leave alone) "); if(isdigit(i=getchar()) && (i -= '0') > 0 && (--i < 8)) { outp(Sport,RESETACIA); outp(Sport,(cr1 = formats[i])); } puts("\n"); } /* * Print the parameters for a given byteform FSW */ mmiiform(fsw) int fsw; { int stopbit; stopbit = 1; printf("%d bits, ",((fsw >> 4) + 7)); switch(fsw >> 2) { case 0: stopbit = 2; case 2: case 6: puts("even"); break; case 1: stopbit = 2; case 3: case 7: puts("odd"); break; case 4: stopbit = 2; default: puts("no"); } printf(" parity, %d stop\n",stopbit); } /* * Put the modem in the on-hook state */ onhook() { outp(MMIICR2,(cr2 &= ~HOOKOFF)); } /* * Put the modem in the off-hook state */ offhook() { outp(MMIICR2,(cr2 |= HOOKOFF)); } /* * Dial the number in the string pointed to by dn */ dial(dn) char *dn; { int i; char c; outp(Sport,RESETACIA); cr2 = (HISPEED|ORIGMODE); outp(MMIICR2,cr2); outp(MMIICR2,(cr2 |= MMSET)); outp(Sport,(cr1 = ACIAMODE)); if(cmdeq(dn,"askme")) /* This entry causes prompt for dn */ { puts("Number, please: "); gets(dn); /* Talk about side-effects! */ } puts("\nWaiting for dial tone..."); offhook(); wait10(300); /* Delay 3 seconds */ puts("\nDialing: "); for (i=0; dn[i] != '\0'; i++) { putchar(dn[i]); spinnum(dn[i]); } puts("\nWaiting for carrier (any key aborts...)\n"); for (i=0; i<600 && !kbhit(); ++i) /* Wait 60 seconds for carrier */ { wait10(10); if(CDO) MICHAR; /* Wake up ACIA */ else { /* Carrier detected */ #ifdef KLUDGE212 if(Sport == MSTAT) #endif outp(MMIICR2,(cr2 |= TXE)); puts("Connected.\n"); return OK; } } onhook(); /* No carrier or aborted, hang up. */ if(kbhit()) getchar(); else puts("No luck..."); puts("\n"); return OK; } /* * Spin the ol' dial... */ spinnum(c) char c; { int num; if(!isdigit(c)) return; num = c - '0'; if(num == 0) num = 10; for( ; num>0; --num) /* Dial pulsing loop */ { onhook(); wait10(6); offhook(); wait10(4); } wait10(60); /* Inter-digit time */ } #endif #ifdef SMODEM /* THIS STUFF IS NOT YET TESTED OR USABLE! */ #define MODEMSTUFF #define AUTODIAL sendcmd(cp) char *cp; { while (*cp) sendline(*cp++); } bye() { onhook(); sleep(10*CLKMHZ); /* wait one seconds */ setbaud(Baudrate); } onhook() { sleep(7 * CLKMHZ); sendcmd(SMATTN); sleep(7 * CLKMHZ); sendcmd(SMHUP); } /* * flip - toggle Smodem to auto-answer mode * Not the original intension of flip. This routine * hasn't been tested so tread with care. */ flip(argc,argp) int argc; char **argp; { int rcnt; if (argc != 1) { oops: printf("\007usage: flip [org | ans | auto]\n"); return ERROR; } if(strcmp("org", argp[0]) == 0 || strcmp("ans", argp[0]) == 0) { printf("Sorry: this version of flip can't do that"); return(OK); } else if (strcmp("auto", argp[0]) == 0) { puts("(^X Aborts) Waiting for Ring: "); for (rcnt = 0; ++r <= NRING;) if (result() > 0) printf("\nRing #%d", r); else return(OK); /* got the rings, go on line */ sendcmd(SMTOANS); if (result() != OK) { puts("\nAnswer Error\n"); return(OK); } Originate = FALSE; /* You may want to turn on echo here, I haven't (yet?) */ term(); return(OK); } else goto oops; */ return OK; } dial(name) char *name; { char *s,*n, num[20]; Originate = TRUE; n=cisubstr(name, "\t")+1; /* I have a line "*SmodemPhones\t*" in my PHONES.T which allows me to * do a 'call *' from the command line to have it ask for the number */ if (*n == '*') { puts("Number to dial: "); gets(num); if (!*num) return (OK); } else { for (s = num; (*s = *n++) != '\t' && *s; ++s) ; *s = '\0'; } /* dial a * option number at the set baud rate! */ nbaud = ((s = cisubstr(name, "\tb")) ? atoi(s + 2) : Baudrate); /* dial at 300 unless baud rate >= 1200 */ setbaud((nbaud < 1200) ? 300 : nbaud); if (MIREADY) readline(0); /* dump any garbage */ sendcmd(SMDIAL); sendcmd(num); sendline('\r'); printf("(^X aborts) Dialing -> %s\n", num); if (result() != OK) return (OK); if (nbaud < 1200 && nbaud > 300) setbaud(nbaud); /* reset to desired baud rate */ printf("\n++ On Line ++ Baudrate = %d ++\n\7", Baudrate); term(); return (OK); } result() { char rcode; sleep(10 * CLKMHZ); /* let the dial get out before allowing abort */ while (!MIREADY) if (CIREADY && CICHAR == '\30') { puts("Aborting"); sendline('\r'); return (ERROR); } switch ((rcode = MICHAR&Wcsmask)) { case '5': nbaud = 1200; setbaud(1200); case '1': readline(2 * CLKMHZ); /* get rid of extra '\r' */ return (OK); case '3': puts("No Carrier\n"); return (ERROR); case '2': return (1); /* if (result() > 0) then ring */ default: printf("Dial error, return code = %c\n", rcode); return (ERROR); } } #endif /* * Delay for a multiple of 10 milliseconds */ wait10(times) int times; { int i, delay; delay = (39 * CLKMHZ); while (times--) { for( i=0; i < delay; i++); } } #ifndef VERSACARD #define READLINE /* Version for 6850, 8250, 8251, 2651, etc. with all bits in one register */ readline(decisecs) { if((Mstatus=inp(Sport))&MIREADYMASK) goto getit; while(--decisecs>=0) { if((Mstatus=inp(Sport))&MIREADYMASK) goto getit; if(CDO) return TIMEOUT; if((Mstatus=inp(Sport))&MIREADYMASK) goto getit; if(CIREADY) { CICHAR; /* dismiss character */ return TIMEOUT; } if((Mstatus=inp(Sport))&MIREADYMASK) goto getit; for(Timeout=T1pause; --Timeout; ) if((Mstatus=inp(Sport))&MIREADYMASK) { getit: if(Mstatus&MIERRORMASK) { MICHAR; /* chuck it */ inp(Sport); /* reset err bits */ return ERROR; } else return MICHAR&Wcsmask; } } return TIMEOUT; } #endif #ifndef MODEMSTUFF setbaud() {} onhook() {} bye() {} readbaud() {} #endif #ifdef VERSACARD /* * Send break */ sendbrk() { outp(MCPORT,(ACIAMODE | SBREAK)); wait10(20); /* Send break for 200 ms */ outp(MCPORT, ACIAMODE); } #endif /* default "autodial" routine */ #ifndef AUTODIAL dial(name) char *name; { char *s; #ifdef VERSACARD outp(MCPORT, RESETACIA); outp(MCPORT, ACIAMODE); #endif if((s=cisubstr(name, "\tb"))) if(!setbaud(atoi(s+2))) printf("Baudrate set to %u: ", Baudrate); printf("%s", name); return OK; } #endif #ifndef READLINE readline(decisecs) { if(MIREADY) return MICHAR&Wcsmask; while(--decisecs>=0) { if(MIREADY) return MICHAR&Wcsmask; if(CIREADY) { CICHAR; /* dismiss character */ return TIMEOUT; } if(MIREADY) return MICHAR&Wcsmask; for(Timeout=T1pause; --Timeout; ) if(MIREADY) return MICHAR&Wcsmask; } return TIMEOUT; } #endif sendline(data) char data; { while(!MOREADY) ; MODOUT(data&Wcsmask); } purgeline() { while(MIREADY) MICHAR; } /* * Dummy chngport() to keep yam1 happy */ chngport(portno) int portno; { } #ifndef APPLICARD /* * This routine is provided to circumvent an apparent "false read" * condition generated by writes from the Softcard. This problem was * long understood to exist for certain 6502 indexed writes, * and now appears to occur on ANY write by the Softcard. (This is * inferred from browsing through the latest (2.21) version of Microsoft's * BIOS for their Softcard.) * * The problem arises when a write instruction puts a valid address on * the address bus for some interval before setting the R/W line to the * "write" state. Since the 6850 ACIA uses the same register address * for displaying received characters and accepting characters to be * transmitted, the false read can result in throwing away a received * character when a write is performed to send a character. This is the * cause, I think, of the random lost characters which I've been attributing * to the "slowness" of YAM. * * The only apparent solution is to do the write to the ACIA from 6502 * code. Recent versions of YAM for the Apple use BIOS calls for console * output, and so will do the right thing if your BIOS is the right * vintage. (Bug Microsoft, not me, to find out if your BIOS is the * right vintage...) For writes to the modem, we're on our own... */ apmout(c) char c; { char *p; unsigned *q; p = 0xf000; /* put a small 6502 subroutine at 6502 address 0. */ *p++ = 0x8d; /* STA */ #ifndef VERSACARD *p++ = Dport & 0xff; *p++ = (Dport >> 8) - 0x20; /* convert to 6502 address */ #else *p++ = MOPORT & 0xff; *p++ = (MOPORT >> 8) - 0x20; /* convert to 6502 address */ #endif *p = 0x60; /* RTS */ p = 0xf045; /* subr. gets its ACC from here. */ *p = c; q = 0xf3d0; *q = 0x0000; /* 6502 address of subroutine */ q = 0xf3de; /* Address of (address of Softcard) */ p = *q; /* Address of Softcard */ *p = 0; /* Poof! Call the 6502 routine */ } #endif #ifdef APPLICARD /* We need some bizarre versions of inp() and outp() for the Applicard, * since it has no direct access to the Apple periphery. */ /* Command codes for the 6502 I/O processor */ #define R1BYTE 6 /* Read a single byte, using absolute addressing */ #define W1BYTE 7 /* Write a single byte, using absolute addressing */ /* 6502 I/O processor interface routines in the Applicard BIOS */ #define RDHSTBYTE 0xffe0 /* read 6502 to A */ #define WRHSTBYTE 0xffe3 /* Write C to 6502 */ #define RDHSTWORD 0xffe6 /* Read 6502 to DE */ #define WRHSTWORD 0xffe9 /* Write DE to 6502 */ char inp(addr) unsigned addr; { addr -= 0x2000; /* my convention is Softcard mapping -- cnvt to 6502 */ call(WRHSTBYTE, 0, 0, R1BYTE, 0); call(WRHSTWORD, 0, 0, 0, addr); return (calla(RDHSTBYTE, 0, 0, 0, 0)); } outp(addr, val) unsigned addr; char val; { addr -= 0x2000; /* my convention is Softcard mapping -- cnvt to 6502 */ call(WRHSTBYTE, 0, 0, W1BYTE, 0); call(WRHSTWORD, 0, 0, 0, addr); call(WRHSTBYTE, 0, 0, val, 0); } #endif