Hi, These serial routines only tested on COM1. Everything is same for COM2 except some constant definitions; should work. The routines were written to handle COM3 and COM4 on the same interrupt lines as COM1 and COM2; however there was some discussion in one of the groups that because of hardware problems this implementation would not work. Not having tried it I don't know. This is a fully buffered implementation. The one routine not defined herein that may be of interest is 'PAGEALLOC'. It allocates a 4Kb page of memory. It is used to allocate space for the serial buffers. Interrupts aren't explicitly chained into the interrupt handlers (IRQ 11 = COM2, IRQ 12 = COM1). This is done as part of the build in the system this code was defined for. Hope this helps, David ------------------------------pic.asi------------------------------------- PIC0ADR EQU 020h PIC1ADR EQU 0A0h NSEOI EQU 020h PIC0VECTOR EQU 78h PIC1VECTOR EQU 70h BIOSPIC0VECTOR EQU 8h BIOSPIC1VECTOR EQU 70h PR_INSERVICE EQU 0bh ; ; Read from PIC ; ; adr = offset in PIC ; pic (defaults to first PIC) = address of pic ; MACRO PICREAD adr,pic local temp ifnb temp = adr else temp = 0 endif ifnb temp = temp + pic else temp = temp + PIC0ADR endif in al,temp ENDM ; ; Write to PIC ; ; adr = offset in PIC ; pic (defaults to first PIC) = address of pic ; MACRO PICWRITE adr,pic local temp ifnb temp = adr else temp = 0 endif ifnb temp = temp + pic else temp = temp + PIC0ADR endif out temp,al ENDM ; ; Delay for a bit ; MACRO PICDELAY jmp $+2 jmp $+2 ENDM ; ; Enable an interrupt by turning off a bit in the PIC ; ; number = bit number ; status = an optional place to store initial status ; pic (defaults to first PIC) = address of pic ; ; MACRO PICENABLE number, status, pic PICREAD 1,pic ifnb ; If we're restoring it exactly later mov status,al ; Save the status endif and al,NOT (1 SHL number) ; Now and out the bit PICWRITE 1,pic ENDM ; ; Disable an interrupt by turning on a bit in the PIC ; ; number = bit number ; status = an optional place to store initial status ; pic (defaults to first PIC) = address of pic ; MACRO PICDISABLE number,status, pic ifnb ; If we previously saved the status mov al,status ; Get it else PICREAD 1,pic or al,1 SHL NUMBER ; And or in the bit endif PICWRITE 1,pic ENDM ; ; While in an interrupt, reset the PIC to enable more interrupts ; ; pic = PIC ; MACRO PICACK pic mov al,NSEOI ; Status bit PICWRITE 0,pic ENDM ---------------------------------serial.asi-------------------------------- MAXBAUD = 115200 MAXPORTS = 4 IBUFSIZE = 512 OBUFSIZE = 512 RR = 0 THR = 0 IER = 1 IIR = 2 LCR = 3 MCR = 4 LSR = 5 MSR = 6 DLLSB = 0 DLMSB = 1 IEMS = 8 IERLS = 4 IETHRE = 2 IEDAV = 1 IIACTIVEMASK = 0f0h IIMASK = 7 IIMSC = 0 IINONE = 1 IITHRE = 2 IIRDAV = 4 IIRLS = 6 LCDLAB = 80h LCBREAK = 40h PARITYODD = 08h PARITYEVEN = 18h PARITYZERO = 28h PARITYONE = 38h PARITYNONE = 0 LCPARMASK = 38h STOPBITS2 = 4 LCWORDMASK = 3 MINWORDLEN = 5 WORD8 = 3 WORD7 = 2 WORD6 = 1 WORD5 = 0 MCRLOOPBACK = 10h MCRENIRQ = 8 MCROUT1 = 4 MCRRTS = 2 MCRDTR = 1 LSRTSRE = 40h LSRTHRE = 20h LSRBREAK = 10h LSRFRAME = 8 LSRPARITY = 4 LSROVERRUN = 2 LSRRECIEVED = 1 LSRMASK = 01eh BUFOVERFLOWED = 1h MSRDCD = 80h MSRRING = 40h MSRDSR = 20h MSRCTS = 10h MSRDCDCHANGE = 8 MSRRINGCHANGE = 4 MSRDSRCHANGE = 2 MSRCTSCHANGE = 1 struc serialbuf size dw ? used dw ? buffer dd ? cqin dd ? cqout dd ? ends serialbuf USEXON = 1 DISABLED = 2 COM1 = 3f8h COM2 = 2f8h COM3 = 3e8h COM4 = 2e8h IMASK = 18h struc serialdef com dw ? flags db DISABLED errflags db 0 iptr dd ? optr dd ? ENDS serialdef -------------------------------serial.asm----------------------------- ; ; Serial.asm ; ; Function: provide interrupt-driven serial communications support ; Handles setting up com port specs ; Handles transmitting\receiving chars from any of the 4 comm ports ; handles serial buffer manipulations ; handle serial OS calls ; IDEAL P386 include "segs.asi" include "os.asi" include "serial.asi" include "page.asi" include "pageall.asi" include "dispatch.ase" include "pic.asi" include "pageall.ase" include "sys.mac" include "boot.ase" include "prints.ase" MACRO circleque xxx local done inc [ebx + SERIALBUF.&xxx] mov eax,[ebx + SERIALBUF.&xxx] push ecx movzx ecx,[ebx + SERIALBUF.SIZE] sub eax,ecx pop ecx cmp eax,[ebx + SERIALBUF.BUFFER] jc short done mov eax,[ebx + SERIALBUF.BUFFER] mov [ebx + SERIALBUF.&xxx],eax done: ENDM circleque PUBLIC serint1,serint2, serialhandler, serialinit, sercon SEGMENT seg386data bufpIn0 serialbuf { size = IBUFSIZE} bufpOut0 serialbuf { size = OBUFSIZE} bufpIn1 serialbuf { size = IBUFSIZE} bufpOut1 serialbuf { size = OBUFSIZE} bufpIn2 serialbuf { size = IBUFSIZE} bufpOut2 serialbuf { size = OBUFSIZE} bufpIn3 serialbuf { size = IBUFSIZE} bufpOut3 serialbuf { size = OBUFSIZE} scon0 serialdef { com = COM1, iptr = bufpIn0, optr = bufpOut0 } scon1 serialdef { com = COM2, iptr = bufpIn1, optr = bufpOut1 } scon2 serialdef { com = COM3, iptr = bufpIn2, optr = bufpOut2 } scon3 serialdef { com = COM4, iptr = bufpIn3, optr = bufpOut3 } sercon dd scon0,scon1,scon2,scon3 ENDS seg386data SEGMENT seg386 ; ; Initialize serial ports and buffers ; PROC serialinit call PageAlloc ; Allocate first buffer page mov esi,eax ; mov eax,OBUFSIZE + IBUFSIZE ; Calculate total space needed mov ecx,MAXPORTS ; ( PAGES) mul ecx ; dec eax ; shr eax,PG_SHIFTSIZE ; mov ecx,eax ; jecxz nomorealloc ; Branch if not to allocate more alloop: ; Assumes all pages will be allocated ; contiguously ALLOCEXT ; Alloc from ext mem call PageAlloc ; Alloc a page loop alloop ; nomorealloc: mov ecx,MAXPORTS ; Now, number of ports mov ebx,offset sercon ; Get list of serial ports ZA esi ; allbufs: mov edi,[ebx] ; Get this port push edi ; mov edi,[edi + SERIALDEF.IPTR] ; Start with input buffer mov [edi + SERIALBUF.BUFFER],esi ; Initialize all buffer ptrs mov [edi + SERIALBUF.CQIN],esi ; to bottom mov [edi + SERIALBUF.CQOUT],esi ; pop edi ; add esi,IBUFSIZE ; Skip past this input buffer mov edi,[edi + SERIALDEF.OPTR] ; Go on to output buffer mov [edi + SERIALBUF.BUFFER],esi ; Initialize all buffer ptrs mov [edi + SERIALBUF.CQIN],esi ; to bottom mov [edi + SERIALBUF.CQOUT],esi ; add ebx,4 ; Next buffer add esi,OBUFSIZE ; Skip past output buffer LOOP allbufs ; Loop mov cl,MAXPORTS ; Number of ports mov ebx,offset sercon ; Serial port list pilp: push ecx ; Save port number mov edi,[ebx] ; Get port info block call disable ; Disable port mov ecx,9600 ; Set it to 9600 mov edx,WORD8 OR PARITYNONE ; 8-bit no parity call setmodes ; pop ecx ; Next port add ebx,4 ; loop pilp ; PICREAD 1 ; Enable the PIC control over and al,NOT IMASK ; these interrupts, we PICWRITE 1 ; mask individual ints via ret ; the IER ENDP serialinit ; ; Interrupt for ports 1 and 3 ; PROC serint2 push edi ; Save regs push eax ; mov edi,offset scon1 ; Service port 1 call serialint ; mov edi,offset scon3 ; Service port 3 call serialint ; PICACK ; Acknowledge int (NSEOI) pop eax ; Restore regs pop edi ; iretd ; Return to program ENDP serint2 ; ; Interrupt for ports 0 and 2 ; PROC serint1 push edi ; Save regs push eax ; mov edi,offset scon0 ; Service port 0 call serialint ; mov edi,offset scon2 ; Service port 2 call serialint ; PICACK ; NSEOI pop eax ; Restore regs pop edi ; iretd ; Return to program ENDP serint1 ; ; Common interrupt service routine ; PROC serialint ; push edx ; mov dl,'!' ; os VF_CHAR ; pop edx push ds ; System data seg push DS386 ; pop ds ; TEST [edi + SERIALDEF.FLAGS],DISABLED; Port disabled? jnz short noint ; Yes, get out push edx ; Save EDX mov dx,[edi + SERIALDEF.COM] ; Get com port proclp: add dl,IIR ; Index to IIR in al,dx ; Get interrupt sub dl,IIR ; Index to buffer and al,IIMASK ; Mask out interrupt type bits cmp al,IINONE ; Get out if none jz short endint ; cmp al,IIRDAV ; Check for DAV jnz short testout ; call inputachar ; Yes, read a char jmp proclp ; testout: cmp al,IITHRE ; Check for THRE jnz short proclp ; call outputachar ; Yes, write a char jmp proclp ; Loop till no more ints buffered endint: pop edx noint: pop ds ret ENDP serialint ; ; Input a char to buffer ; PROC inputachar push ebx ; Save EBX mov ebx,[edi + SERIALDEF.IPTR] ; Get input buffer mov ax,[ebx + SERIALBUF.USED] ; See if fll cmp ax,[ebx + SERIALBUF.SIZE] ; in al,dx ; Get input char jc short gchar ; Not full, go put it or [edi + SERIALDEF.ERRFLAGS],BUFOVERFLOWED; Buffer full! jmp short fininput ; Check other erros gchar: inc [ebx + SERIALBUF.USED] ; Inc used count push ebx ; Put char in buffer mov ebx,[ebx + SERIALBUF.CQIN] ; mov [ebx],al ; pop ebx ; circleque CQIN ; Update que pointer fininput: add dl, LSR ; Index LSR in al,dx ; Read error bits sub dl,LSR ; and al,LSRMASK ; or [edi + SERIALDEF.ERRFLAGS],al ; Or into status flags pop ebx ret ENDP inputachar ; ; Output a char from buffer ; PROC outputachar push ebx ; Save ebx mov ebx,[edi + SERIALDEF.OPTR] ; Get output buffer test [ebx + SERIALBUF.USED],-1 ; See if buffer mt jz short finoutput ; yes, get out dec [ebx + SERIALBUF.USED] ; Else dec the count push ebx ; mov ebx,[ebx + SERIALBUF.CQOUT] ; Get cq deletion mov al,[ebx] ; Grab a char out dx,al ; Put it out the port pop ebx ; circleque CQOUT ; Update que pointer finoutput: pop ebx ret ENDP outputachar ; ; Add a char to the output buffer ; PROC putachar push ebx ; mov ebx,[edi + SERIALDEF.OPTR] ; Get output buffer mov ax,[ebx + SERIALBUF.SIZE] ; See if full cmp ax,[ebx + SERIALBUF.USED] ; jbe short toomanychars ; Yes, get out inc [ebx + SERIALBUF.USED] ; Inc buffer count push ebx ; Put char in buffer mov ebx,[ebx + SERIALBUF.CQIN] ; mov [ebx],dl ; pop ebx ; circleque CQIN ; Update queue pointer clc ; No errors jmp short didoutput ; toomanychars: stc ; didoutput: pushfd ; Save return status mov dx,[edi + SERIALDEF.COM] ; Get comm port add dl,LSR ; Index to LSR in al,dx ; Check THRE bit sub dl,LSR ; and al,LSRTHRE ; cmp al,LSRTHRE ; jnz short sending ; Not set, send in progress call outputachar ; Else do the first send sending: popfd ; Return status pop ebx ret ENDP putachar ; ; Get a char from input buffer ; PROC getachar push ebx ; mov ebx,[edi + SERIALDEF.IPTR] ; Input buffer pointer test [ebx + SERIALBUF.USED],-1 ; Anything there? jz short nothingtoget ; No, get out dec [ebx + SERIALBUF.USED] ; Decrement count push ebx ; Get char mov ebx,[ebx + SERIALBUF.CQOUT] ; mov al,[ebx] ; pop ebx ; push eax ; circleque CQOUT ; Update queue pop eax ; pop ebx ; clc ; ret ; nothingtoget: pop ebx stc ret ENDP getachar ; ; Receive buffer status ; PROC receivebufferstatus mov ebx,[edi + SERIALDEF.IPTR] movzx eax,[ebx + SERIALBUF.USED] ; EAX gets bytes in buf movzx ebx,[ebx + SERIALBUF.SIZE] ; EBX gets buf size clc ret ENDP receivebufferstatus ; ; XMIT buffer status ; PROC xmitbufferstatus mov ebx,[edi + SERIALDEF.OPTR] ; mov ax,[ebx + SERIALBUF.SIZE] ; eax gets space left in buf sub ax,[ebx + SERIALBUF.USED] ; movzx ebx,[ebx + SERIALBUF.SIZE] ; EBX gets buf size cwde ; clc ret ENDP xmitbufferstatus ; ; Clear xmit buffer ; PROC clearxmitbuffer push ebx mov ebx,[edi + SERIALDEF.OPTR] ; Point at output buffer mov eax,[ebx + SERIALBUF.BUFFER] ; Get buffer start cli mov [ebx + SERIALBUF.USED],0 ; Wipe the count mov [ebx + SERIALBUF.CQIN],eax ; And reposition the queues mov [ebx + SERIALBUF.CQOUT],eax ; sti pop ebx clc ret ENDP clearxmitbuffer ; ; Clear receive buffer ; PROC clearreceivebuffer push ebx mov ebx,[edi + SERIALDEF.IPTR] ; Get input buffer ptr mov eax,[ebx + SERIALBUF.BUFFER] ; And input buffer cli mov [ebx + SERIALBUF.USED],0 ; Wipe count mov [ebx + SERIALBUF.CQIN],eax ; Reset buffer pointers mov [ebx + SERIALBUF.CQOUT],eax ; sti pop ebx clc ret ENDP clearreceivebuffer ; ; Enable a serial line ; PROC enable push edx ; mov dx,[edi + SERIALDEF.COM] ; Point at IIR add dl,IIR ; in al,dx ; and al,IIACTIVEMASK ; Check if there jnz short cantenable ; No, no such port sub dl,IIR-IER ; Else point at IER mov al,IETHRE OR IEDAV ; Interrupts to enable out dx,al ; Do it and [edi + SERIALDEF.FLAGS], NOT DISABLED ; Clear disable flag pop edx ; clc ; No errors ret cantenable: pop edx stc ret ENDP enable ; ; Disable a serial line ; PROC disable push edx ; mov dx,[edi + SERIALDEF.COM] ; Get COMM port add dl,IER ; Point at IER sub al,al ; No interrupts allowed out dx,al ; or [edi + SERIALDEF.FLAGS],DISABLED; Set disable flag pop edx ; No errors clc ret ENDP disable ; ; Set modes ; PROC setmodes push ebx push ecx push edx cli mov ebx,edx ; EBX = modes mov dx,[edi + SERIALDEF.COM] ; Point at MCR add dl,MCR ; mov al,bh ; or al,MCRENIRQ ; Must have this bit for interrupts out dx,al ; sub dl,MCR - LCR ; Point at LCR mov eax,MAXBAUD ; Divide Maxbaud by baud rate push edx ; And get timer divide count sub edx,edx ; div ecx ; mov ecx,eax ; pop edx ; mov al,bl ; Load LCR and turn on LAB or al,LCDLAB ; out dx,al ; Load LCR sub dl,LCR ; Point at DLAB LSB mov al,cl ; Output LSB out dx,al ; mov al,ch ; MSB inc dx ; Point at DLAB MSB out dx,al ; Out MSB mov al,bl ; Get LCR data add dl,LCR-1 ; And LCR reg out dx,al ; Turn DLAB latch off sti pop edx pop ecx pop ebx clc ret ENDP setmodes ; ; Get serial line modes ; PROC getmodes push edx mov dx,[edi + SERIALDEF.COM] ; Point at MSR add dl,MSR ; in al,dx ; AH = MSR mov ah,al ; mov al,[edi + SERIALDEF.ERRFLAGS] ; AL = ERR FLAGS mov [edi + SERIALDEF.ERRFLAGS],0 ; Clear Err Flags pop edx ; No errors clc ret ENDP getmodes ; ; Serial handler ; PROC serialhandler cmp bl,MAXPORTS ; Valid port? jnc nofunction ; No- bad request push ds ; Save DS push DS386 ; DS gets system data seg pop ds ; push edi ; Save other regs push edx ; push ecx ; push eax ; mov eax,ebx ; sub ah,ah ; EDI = serial control block cwde ; mov edi,[sercon + 4 * eax] ; pop eax ; Restore function number push 0 ; Dispatch function call TableDispatch ; dd 9 dd enable dd disable dd putachar dd getachar dd setmodes dd getmodes dd clearxmitbuffer dd clearreceivebuffer dd xmitbufferstatus dd receivebufferstatus pop ecx ; Pop regs pop edx ; pop edi ; pop ds ; ret ENDP serialhandler ENDS seg386 END