Category : Network Files
Archive   : KA9Q.ZIP
Filename : 8250.C

 
Output of file : 8250.C contained in archive : KA9Q.ZIP
/* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC */
/* hacked to support BIOS interaction for NET PC9801 ala JK1NNT by N3EUA */

#include "config.h"
#if !defined(PLUS)

#include
#include "global.h"
#include "asy.h"
#include "8250.h"
#include "iface.h"

struct asy asy[ASY_MAX];
unsigned nasy;

unsigned h2ivec[ASYHANDLE_MAX] ;
unsigned nhandlers ;

struct ivec ivec[NIVECS] ;

#ifdef PC9801
int work, f_handle, jkintdos();
#define mask 0x00ff
#endif

/* ASY interrupt handlers */
#ifndef PC9801
extern void asy0vec(),asy1vec(),asy2vec(),asy3vec(),asy4vec();
void (*handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
#endif

/* Initialize asynch port "dev" */
int
asy_init(dev,arg1,arg2,bufsize)
int16 dev;
char *arg1,*arg2; /* Attach args for address and vector */
unsigned bufsize;
{
register unsigned base;
register struct fifo *fp;
register struct asy *ap;
unsigned vec ;
void (*getirq())();
char i_state;

ap = &asy[dev];
ap->addr = htoi(arg1);
ap->vec = htoi(arg2);
/* Set up receiver FIFO */
fp = &ap->fifo;
if((fp->buf = malloc(bufsize)) == NULLCHAR){
printf("asy%d: No space for rx buffer\r\n",dev);
fflush(stdout);
return -1;
}
fp->bufsize = bufsize;
fp->wp = fp->rp = fp->buf;
fp->cnt = 0;

base = ap->addr;
ap->urgent = NULLCHAR; /* For SLFP urgent data */

#ifdef PC9801
f_handle = auxopen();
#else
/* Purge the receive data buffer */
(void)inportb(base+RBR);

i_state = disable();

/* Set up interrupt vector structure if necessary */
vec = ap->vec ;
if (ivec[vec].ichain == NULLASY) {
if (nhandlers == ASYHANDLE_MAX) {
printf("asy%d: No more interrupt handlers\n",dev) ;
return ;
}

/* Save original interrupt vector and mask */
ivec[vec].oldvec = getirq(ap->vec) ;
ivec[vec].oldmask = getmask(ap->vec) ;

/* Set interrupt vector to SIO handler */
setirq(ap->vec,handle[nhandlers]);

/* Set up correspondence between handler and interrupt */
h2ivec[nhandlers] = vec ;

nhandlers++ ; /* step to next handler */

/* Start chain */
ivec[vec].ichain = &asy[dev] ;
ap->ichain = NULLASY ; /* terminate the chain */
}
else { /* Already a handler for this; just put in chain */
ap->ichain = ivec[vec].ichain ; /* Put at head */
ivec[vec].ichain = &asy[dev] ;
}

/* Update vector reference count */
ivec[vec].refcnt++ ;

/* Save original control bits */
ap->save.lcr = inportb(base+LCR);
ap->save.ier = inportb(base+IER);
ap->save.mcr = inportb(base+MCR);

/* save speed bytes */
setbit(base+LCR,LCR_DLAB);
ap->save.divl = inportb(base+DLL);
ap->save.divh = inportb(base+DLM);
clrbit(base+LCR,LCR_DLAB);

/* Set line control register: 8 bits, no parity */
outportb(base+LCR,(char)LCR_8BITS);

/* Turn on receive interrupt enable in 8250, leave transmit
* and modem status interrupts turned off for now
*/
outportb(base+IER,(char)IER_DAV);

/* Set modem control register: assert DTR, RTS, turn on 8250
* master interrupt enable (connected to OUT2)
*/
outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));

/* Enable interrupt */
maskon(ap->vec);
restore(i_state);
#endif /* PC9801 */
}
int
asy_stop(iface)
struct interface *iface;
{
register unsigned base;
register struct asy *ap;
unsigned vec ;
char i_state;

ap = &asy[iface->dev];
base = ap->addr;

#ifndef PC9801
/* Purge the receive data buffer */
(void)inportb(base+RBR);

/* See if this is the last asy using this interrupt, */
/* and restore the vector and interrupt mask if it is */
vec = ap->vec ;
if (--ivec[vec].refcnt == 0) {
i_state = disable();
setirq(ap->vec,ivec[vec].oldvec);
if(ivec[vec].oldmask)
maskon(ap->vec);
else
maskoff(ap->vec);
restore(i_state);
}

/* Restore original interrupt vector and 8259 mask state */
/* Restore speed regs */
setbit(base+LCR,LCR_DLAB);
outportb(base+DLL,ap->save.divl); /* Low byte */
outportb(base+DLM,ap->save.divh); /* Hi byte */
clrbit(base+LCR,LCR_DLAB);

/* Restore control regs */
outportb(base+LCR,ap->save.lcr);
outportb(base+IER,ap->save.ier);
outportb(base+MCR,ap->save.mcr);
#endif /* PC9801 */
}
/* Asynchronous line I/O control */
asy_ioctl(interface,argc,argv)
struct interface *interface;
int argc;
char *argv[];
{
if(argc < 1){
printf("%d\r\n",asy[interface->dev].speed);
return 0;
}
return asy_speed(interface->dev,atoi(argv[0]));
}
/* Set asynch line speed */
int
asy_speed(dev,speed)
int16 dev;
int speed;
{
#ifdef PC9801
register int s_code, speed1;

if (speed ==0 || speed >= 10000 || dev >= nasy)
return -1;

speed1 = 75;
for (s_code = 0; speed >= speed1; s_code++) speed1 *= 2;
speed1 /= 2;
asy[dev].speed = speed1;
set_speed(s_code);
#else
register unsigned base;
register int divisor;
char i_state;

if(speed == 0 || dev >= nasy)
return -1;

base = asy[dev].addr;
asy[dev].speed = speed;

divisor = BAUDCLK / (long)speed;

i_state = disable();

/* Purge the receive data buffer */
(void)inportb(base+RBR);

/* Turn on divisor latch access bit */
setbit(base+LCR,LCR_DLAB);

/* Load the two bytes of the register */
outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */

/* Turn off divisor latch access bit */
clrbit(base+LCR,LCR_DLAB);

restore(i_state);
return 0;
#endif /* PC9801 */
}

/* Send a buffer to serial transmitter */
asy_output(dev,buf,cnt)
unsigned dev;
char *buf;
unsigned short cnt;
{
#ifdef PC9801
int b_cnt, b_buf;

if (dev >= nasy) return;
for (b_cnt = 0; b_cnt < cnt; b_cnt++) {
b_buf = buf[b_cnt];
aput(b_buf);
}
#else
register struct dma *dp;
unsigned base;
char i_state;

if(dev >= nasy)
return;
base = asy[dev].addr;
dp = &asy[dev].dma;
i_state = disable();
if(dp->flags){
restore(i_state);
return; /* Already busy */
}
dp->data = buf;
dp->cnt = cnt;
dp->flags = 1;
/* Enable transmitter buffer empty interrupt and simulate
* an interrupt; this will get things rolling.
*/
setbit(base+IER,IER_TxE);
asytxint(dev);
restore(i_state);
#endif /* PC9801 */
}
/* Receive characters from asynch line
* Returns count of characters read
*/
int16
asy_recv(dev,buf,cnt)
int16 dev;
char *buf;
unsigned cnt;
{
unsigned tot,n;
#ifdef PC9801
int ch;

for (tot = 0; tot < cnt; tot++) {
if (auxstat()) ch = aget();
else break;
buf[tot] = ch;
}
return tot;
}

#else
int kbread();
char i_state;
struct fifo *fp;

fp = &asy[dev].fifo;
tot = 0;
/* Read from serial I/O input buffer */
i_state = disable();
for(;;){
n = min(cnt,fp->cnt);
if(n == 0)
break;
n = min(n,&fp->buf[fp->bufsize] - fp->rp);
memcpy(buf,fp->rp,n);
fp->rp += n;
if(fp->rp >= &fp->buf[fp->bufsize])
fp->rp = fp->buf;
fp->cnt -= n;
buf += n;
tot += n;
cnt -= n;
}
restore(i_state);
return tot;

}
/* Interrupt handler for 8250 asynch chip */
void
asyint(handler)
unsigned handler;
{
register unsigned base;
register char iir;
register unsigned dev ;
struct asy *ap ;
int someint ;

/* The following bears some explaining. Because the PC and AT
* (but not the PS/2) uses edge triggered interrupts, we need
* to assure that the shared interrupt line makes a low-going
* transition before we issue an EOI. If we don't, one of the
* UARTs could raise its interrupt line again after we serviced
* it, but before we cleared all the other UARTs' interrupts,
* and this would be missed by the interrupt controller in
* edge triggered mode. This should still work fine on a
* PS/2.
* Many thanks to Dan Dodge of Quantum Software, Ltd., for
* suggesting this trick. dmf
*/

do {

someint = 0 ; /* No interrupt detected yet on this pass */

ap = ivec[h2ivec[handler]].ichain ; /* Start at head of chain */

while (ap != NULLASY) {
dev = ap - asy ;
base = asy[dev].addr;
while(((iir = inportb(base+IIR)) & IIR_IP) == 0) {
someint = 1 ; /* Detected an interrupt */
switch(iir & IIR_ID){
case IIR_RDA: /* Receiver interrupt */
asyrxint(dev);
break;
case IIR_THRE: /* Transmit interrupt */
asytxint(dev);
break;
}
}
ap = ap->ichain ; /* step to next in chain */
}

} while (someint != 0) ;
}
/* Process 8250 receiver interrupts */
static
asyrxint(dev)
unsigned dev;
{
unsigned base;
register struct fifo *fp;
char c;

base = asy[dev].addr;
fp = &asy[dev].fifo;
while(inportb(base+LSR) & LSR_DR){
c = inportb(base+RBR);
/* Process incoming data;
* If buffer is full, we have no choice but
* to drop the character
*/
if(fp->cnt != fp->bufsize){
*fp->wp++ = c;
if(fp->wp == &fp->buf[fp->bufsize])
/* Wrap around */
fp->wp = fp->buf;
fp->cnt++;
}
}
}
/* Handle 8250 transmitter interrupts */
static
asytxint(dev)
unsigned dev;
{
register struct dma *dp;
register unsigned base;
unsigned urg; /* urgent SLFP data, or == 256 */

base = asy[dev].addr;
dp = &asy[dev].dma;
if(!dp->flags){
/* "Shouldn't happen", but disable transmit
* interrupts anyway
*/
clrbit(base+IER,IER_TxE);
return; /* Nothing to send */
}
while(inportb(base+LSR) & LSR_THRE){
/* Send any pending urgent data */
if (asy[dev].urgent != NULLCHAR)
if ((urg = (*asy[dev].urgent)(dev)) < 256) {
outportb(base+THR,urg);
continue;
}
dp->last_octet = *dp->data;
outportb(base+THR,*dp->data++);
if(--dp->cnt == 0){
dp->flags = 0;
/* Disable transmit interrupts */
clrbit(base+IER,IER_TxE);
/* Call completion interrupt here */
break;
}
}
}

/* Set bit(s) in I/O port */

setbit(port,bits)
unsigned port;
char bits;
{
outportb(port,(char)inportb(port)|bits);
}
/* Clear bit(s) in I/O port */
clrbit(port,bits)
unsigned port;
char bits;
{
outportb(port,(char)(inportb(port) & ~bits));
}
#endif /* PC9801 */

int
stxrdy(dev)
int16 dev;

{
return(!asy[dev].dma.flags);
}

#ifdef PC9801
auxopen()
{
char *device;
device = "AUX";
work = jkintdos(0x3d02,0,device);
return (work);
}

auxstat()
{
work = jkintdos(0x4406,f_handle,0) & mask;
return (work);
}

aget()
{
work = jkintdos(0x0300,0,0) & mask;
return (work);
}

aput(ch)
int ch;
{
jkintdos(0x0400,0,ch);
}
#endif /* PC9801 */
#endif /* !defined(PLUS) */


  3 Responses to “Category : Network Files
Archive   : KA9Q.ZIP
Filename : 8250.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/