; * * * * * * * * * * * * * * * version 2.1 * * * * * * * * * * * * * * * ; [2] Add a de-initialization routine for the serial port, to restore ; changed interrupt vectors, etc. ; RonB,12/23/83 ; [1] Add I/O support for the NEC Advanced Personal Computer ; RonB,12/23/83 ; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * * ; This module contains all the low level communications port I/O ; routines. if necapc ;[1] begin cseg $ ; Interrupt vector locations, in data segment 0 mnioff equ 84h ; sio interrupt offset mniseg equ 86h ; sio interrupt segment ; 8259 Interrupt controller (master) iccmd equ 20h ; interrupt command register icmask equ 22h ; interrupt mask register ; 8259 commands and masks icEOI equ 20h ; end of interrupt (command) ictmof equ 08h ; disable timer (mask) icmnof equ 02h ; disable RS232 (mask) ; 8253-5 Interval Timer tmdata equ 2bh ; baud set (chan 1) tmcmd equ 2fh ; baud timer command port ; 8253 Timer commands tmch1 equ 76h ; select & init timer channel 1 ; 8251A USART controller mndata equ 30h ; data port mnsts1 equ 32h ; in status port mnsts2 equ 34h ; in nec special status port mncmd equ 32h ; out command port mnmsk equ 34h ; out interrupt mask port mntdc equ 36h ; out tdc (whatever that is) ; 8251 status port 1 bits mninp equ 02h ; receive ready value mnout equ 01h ; send ready value mndsr equ 80h ; data set ready ; 8251 status port 2 bits mncts equ 04h ; clear to send ; 8251 initialization instructions ; command instructions ctxe equ 01h ; transmit enable cdtr equ 02h ; dtr signal high crxe equ 04h ; receive enable cbrk equ 08h ; send break cerr equ 10h ; error reset crts equ 20h ; rts signal high cmode equ 40h ; reset - go to mode instruction format chunt equ 80h ; hunt for sync characters ; mode instructions m1x equ 01h ; baud rate factor: 1x m16x equ 02h ; 16x m64x equ 03h ; 64x m5d equ 00h ; data bits: 5 m6d equ 04h ; 6 m7d equ 08h ; 7 m8d equ 0Ch ; 8 mpn equ 00h ; parity: none mpo equ 10h ; odd mpe equ 30h ; even m1s equ 40h ; stop bits: 1 m15s equ 80h ; 1.5 m2s equ 0C0h ; 2 ; 8251 interrupt mask port bits txmsk equ 01h ; disable transmit complete interrupt rxmsk equ 02h ; disable receive complete interrupt tbemsk equ 04h ; disable transmit buffer empty interrupt ; Test if port is ready to send next char. Returns RSKP if ready. ; Trashes dx. outwt: push ax mov dx,mnsts1 in al,dx and al,mndsr+mnout sub al,mndsr+mnout jnz outwt1 mov dx,mnsts2 in al,dx and al,mncts jnz outwt2 outwt1: pop ax ret outwt2: pop ax jmp rskp ; Output data to port. Trashes DX and prints char in AL. outchr: mov dx,mndata out dx,al ret ; Output the character in AL, checking first to make sure the port is clear. prtout: push dx prtou2: call outwt ; Wait til the port is ready. jmp prtou2 call outchr ; Output it. pop dx ret ; Test if data is available from port. instat: cmp mnchrn,0 ; Any chars in the buffer? jnz inst2 ret inst2: jmp rskp ; Input data from port. Preserves all registers and returns char in ; AL. Gets the char from the ring buffer. Assumes a char is ; already there. inchr: push bx cli ; Disable interrupts while were are playing. dec mnchrn ; Decrement the number of chars in the buffer. mov bx,mnchop ; Get the pointer into the buffer. inc bx ; Increment to the next char. cmp bx,offset mnchrs+mnchnd ; Past the end? jb inchr2 lea bx,mnchrs ; If so wrap around to the start. inchr2: mov mnchop,bx ; Save the updated pointer. mov al,[bx] ; Get the character. sti ; All done, we can restore interrupts. pop bx ret ; This routine handles the interrupts on input. mnint: cli push ax ; Save anything we might mess up. push bx push dx push ds mov ax,cs:mndseg ; Set old DSEG as our present segment. mov ds,ax call mnproc ; Process the character. mov dx,iccmd mov al,icEOI ; signal end of interrupt to controller out dx,al pop ds pop dx pop bx pop ax iret ; Restore and return from the interrupt. ; This routine (called by MNINT) gets a char from the serial port ; and puts it in the ring buffer. mnproc: mov dx,mnsts1 in al,dx ; Get the port status. and al,mninp ; Is a character waiting? jnz mnpro2 ; Yes, go take care of it. ret ; No, just a false alarm. mnpro2: mov dx,mndata in al,dx ; Read the char. cmp mnchrn,mnchnd ; Is the buffer full? je mnperr ; If so, take care of the error. inc mnchrn ; Increment the character count. mov bx,mnchip ; Get the buffer input pointer. inc bx ; Increment it. cmp bx,offset mnchrs+mnchnd ; Past the end? jb mnpro3 lea bx,mnchrs ; Yes, point to the start again. mnpro3: mov mnchip,bx ; Save the pointer. mov [bx],al ; Put the character in the buffer. ret mnperr: ret ; Just return on an error for now. mndseg dw 0 ; Storage in CSEG to point to DSEG ; (for use by interrupt handler) ; serini - this routine initializes all devices that need it. serini: cmp mnintf,0 ; Skip initialization if already done jz serin1 ret serin1: ; this code can only be executed once mov mnintf,0FFh ; set flag for initialization done mov dx,icmask in al,dx ; get current interrupt mask mov mnxmsk,al ; save it for restore or al,ictmof+icmnof ; mask off timer and sio interrupts out dx,al mov ax,ds ; save data segment in cseg mov cs:mndseg,ax ; for use by the interrupt handler push es mov ax,0 ; point to zero page to replace mov es,ax ; the sio interrupt vector mov ax,es:.mniseg ; after first saving the current vector mov mnxseg,ax mov ax,es:.mnioff mov mnxoff,ax cli mov ax,cs mov es:.mniseg,ax mov ax,offset mnint mov es:.mnioff,ax sti pop es serin2: ; this code can be repeated as necessary call stmode ;set mode & baud to defaults call stbaud mov dx,mntdc mov al,00h ; set tdc register (whatever that is) out dx,al mov dx,mndata ; dummy read to clear buffer in al,dx mov dx,mnmsk mov al,txmsk+tbemsk ; set interrupt mask (enable read int) out dx,al mov dx,icmask in al,dx ; enable sio interrupts and al,not icmnof out dx,al ret ; serfin - this routine is used to "undo" what serini has done, called ; just before exiting back to cp/m. serfin: cmp mnintf,0 ; has initialization been done? jz serfi2 ; no, don't undo it cli mov dx,icmask mov al,mnxmsk ; restore the old interrupt mask out dx,al push es mov ax,0 mov es,ax mov ax,mnxseg ; restore sio interrupt vector mov es:.mniseg,ax mov ax,mnxoff mov es:.mnioff,ax pop es sti serfi2: ret ; set the parity, number of data bits, and number of stop bits stmode: mov dx,mncmd mov al,0 ; recommended reset procedure: out dx,al ; three 0's followed by a cmode mov al,0 out dx,al mov al,0 out dx,al mov al,cmode ; enable mode setting out dx,al mov al,m1s ; 1 stop, no parity, 8 data, 16x baud add al,mpn ; Note: these adds are distinct to add al,m8d ; allow the 8251 time to reset add al,m16x out dx,al mov al,crts+cerr+crxe+cdtr+ctxe ; RTS & DTR high, Rx & Tx enabled out dx,al ret ; set the baud rate stbaud: mov al,mnbaud ; get the baud rate information cmp al,12 ; check for valid range (0-12) ja stb02 mov bx,offset baudtb ; get address of baud rate table add al,al ; compute word offset mov ah,0 add bx,ax mov dx,tmcmd mov al,tmch1 ; select timer channel 1 out dx,al mov dx,tmdata mov ax,[bx] ; get value out dx,al ; output low byte mov al,ah out dx,al ; output high byte stb02: ret dseg $ ; Serial port default parameters mnbaud db 6 ; 300 baud ; Interval Timer values (assumes 16x baud rate mode) baudtb dw 0C00h ; 50 baud 0 dw 0800h ; 75 baud 1 dw 0600h ; 100 baud 2 dw 0574h ; 110 baud 3 dw 0400h ; 150 baud 4 dw 0300h ; 200 baud 5 dw 0200h ; 300 baud 6 dw 0100h ; 600 baud 7 dw 0080h ; 1200 baud 8 dw 0040h ; 2400 baud 9 dw 0020h ; 4800 baud 10 dw 0010h ; 9600 baud 11 dw 0008h ; 19200 baud 12 mnintf db 0 ; Flag saying whether we've init'ed port. mnxmsk db 0 ; 8259 interrupt mask storage mnxseg dw 0 ; system sio interrupt vector mnxoff dw 0 mnchnd equ 512 ; Size of circular buffer. mnchrs rb mnchnd ; Circular character buffer for input. mnchip dw mnchrs-1+mnchnd ; Input pointer into character buffer. mnchop dw mnchrs-1+mnchnd ; Output pointer into character buffer. mnchrn dw 0 ; Number of chars in the buffer. endif ;necapc ;[1] end if rainbo CSEG $ mnstat EQU 042H ; Status port. mndata EQU 040H ; Data port. mnctrl EQU 002H ; Control port. ; Interrupt vector locations. These are all in data segment 0. mnoff EQU 90H ; Main data port interrupt routine offset. mnseg EQU 92H ; Main data port interrupt routine segment. mnchnd EQU 80H ; Size of circular buffer. output EQU 04H ; Bit for output ready. input EQU 01H ; Bit for input ready. ; Input data from port. Preserves all ACs and returns char in ; AL. Gets the char from the ring buffer. Assumes a char is ; already there. inchr: push bx cli ; Disable interrupts while were are playing. dec mnchrn ; Decrement the number of chars in the buffer. mov bx, mnchop ; Get the pointer into the buffer. inc bx ; Increment to the next char. cmp bx, OFFSET mnchrs+mnchnd ; Past the end? jb inchr2 lea bx, mnchrs ; If so wrap around to the start. inchr2: mov mnchop, bx ; Save the updated pointer. mov al, [bx] ; Get the character. sti ; All done, we can restore interrupts. pop bx ret ; Output data to port. Trashes DX and prints char in AL. outchr: mov dx, mndata out dx, al ret ; Test if data is available from port. instat: cmp mnchrn, 0 ; Any chars in the buffer? jnz inst2 ret inst2: jmp rskp ; Test if port is ready to send next char. Returns RETSKP if ready. ; Trashes dx. outwt: push ax mov dx, mnstat in al, dx test al, output pop ax jnz outwt2 ret outwt2: jmp rskp ; Output the character, checking first to make sure the port is clear. prtout: push dx prtou2: call outwt ; Wait til the port is ready. jmp prtou2 call outchr ; Output it. pop dx ret ; This routine handles the interrupts on input. mnint: push ax ; Save anything we might mess up. push bx push dx push ds mov ax, cs:mndseg ; Set old DSEG as our present segment. mov ds, ax call mnproc ; Process the character. mov dx, mnstat ; Get the status port. mov al, 38H out dx, al ; Tell the port we finished with the interrupt. pop ds pop dx pop bx pop ax iret ; Restore and return from the interrupt. ; This routine (called by MNINT) gets a char from the main port ; and puts it in the infamous circular buffer. mnproc: mov dx, mnstat in al, dx ; Get the port status. test al, input ; Any there? jnz mnpro2 ; Yup, go take care of it. ret ; Whoops, just a false alarm. mnpro2: mov al, 1 ; Point to RR1. out dx, al in al, dx ; Read RR1. mov ah, al ; Save it. mov al, 30H ; Reset any errors. out dx, al mov dx, mndata in al, dx ; Read the char. cmp mnchrn, mnchnd ; Is the buffer full? je mnperr ; If so, take care of the error. inc mnchrn ; Increment the character count. mov bx, mnchip ; Get the buffer input pointer. inc bx ; Increment it. cmp bx, OFFSET mnchrs+mnchnd ; Past the end? jb mnpro3 lea bx, mnchrs ; Yes, point to the start again. mnpro3: mov mnchip, bx ; Save the pointer. mov [bx], al ; Put the character in the buffer. ret mnperr: ret ; Just return on an error for now. mndseg DW 0 ; Storage in CSEG to point to DSEG (for ; interrupt handler). ; Init the communications control port (02H) to set RTS & DTR high serin3: mov dx, mnctrl ;point to comm control port mov al, 0F0H ;set RTS & DTR high out dx, al ret ; Init the 7201 for 8 bits, no parity, and 1 stop bit. serini: cmp mnintf, 0 ; Have we initialized already? jz serin2 ; No, proceed. ret ; Yes, don't bother to do it again. serin2: mov mnintf, 0FFH ; Say we've already init'ed. mov ax, ds mov cs:mndseg, ax ; Save the data segment somewhere in CSEG. push ds ; Save the data segment. mov ax, 0 mov ds, ax ; We want DSEG = 0. cli ; Turn off interrupts. mov ax, OFFSET mnint ; Point to the interrupt routine offset. mov .mnoff, ax ; Put in the main port interrupt offset addr. mov ax, cs ; Get our code segment. mov .mnseg, ax ; Put in the main port interrupt segment addr. sti ; Restore interrupts. pop ds ; Restore data segment. mov dx, mnstat ; Point to status port. mov al, 18H out dx, al ; Reset the port. mov al, 14H out dx, al ; Select register 4. mov al, 44H ; 16X clock, 1 stop bit, no parity. out dx, al mov al, 13H out dx, al ; Select register 3. mov al, 0C1H ; 8 bits/char, RX enable. out dx, al mov al, 15H out dx, al ; Select register 5. mov al, 0EAH ; 8 bits/char, TX enable, RTS and DTR. out dx, al mov al, 11H out dx, al ; Select register 1. mov al, 18H out dx, al ; Enable interrupt processing on this port. ret serfin: ret ; Nothing to deinitialize on Rainbow ;[2] DSEG $ mnintf DB 0 ; Flag saying whether we've init'ed port. mnchrn DB 0 ; Number of chars in the buffer. mnchrs RB 80H ; Circular character buffer for input. mnchip DW mnchrs-1+mnchnd ; Input pointer into character buffer. mnchop DW mnchrs-1+mnchnd ; Output pointer into character buffer. endif ;rainbo