3C55 OPT opt 3C55 ; 3C55 ;*************************************************************** 3C55 ; 3C55 ;IBM-PC Serial Port Drivers for BBC BASIC (86) 3C55 ;DJM - 02 Dec 86, RTR - 25 Sep 88. 3C55 ;Provides access to either the primary or alternate serial port. 3C55 ; 3C55 ; 3C55 ;Functions 3C55 ; 3C55 ;_com_init Initialise specified port to 8 data bits, 1 stop bit 3C55 ; and no parity. Port is passed in CX (0 = COM1, 3C55 ; 1 = COM2), and baud rate in BX (50 - 38400 baud). 3C55 ; All subsequent I/O uses this port. 3C55 ; Returns 1 if successful, 0 if error. 3C55 ; 3C55 ;_com_exit Disable interrupts and restore interrupt vector. 3C55 ; 3C55 ;_com_get Gets next byte from port, or -ve if error 3C55 ; -255 buffer overflow ; -254 overrun error 3C55 ; -252 parity error ; -248 framing error 3C55 ; -240 break interrupt ; -128 timeout 3C55 ; 3C55 ;_com_put Outputs the byte in AL to the port. Returns char 3C55 ; or -1 if timeout. 3C55 ; 3C55 ;_com_stat Returns 1 if rx byte waiting, else 0. 3C55 ; 3C55 ; 3C55 ;*************************************************************** 3C55 ; 3C55 ;Initialise the serial port to 8 data bit, 1 stop bit, no parity 3C55 ;and set up communications interrupts. 3C55 ; 3C55 ;Parameters 3C55 ;1) BX = Baud rate (50-38400) 3C55 ;2) CX = 0 for Primary card (COM1), 1 for Alternate card (COM2) 3C55 ;Returns 3C55 ;1 if port OK, 0 if error 3C55 ; 3C55 ._com_init 3C55 0E push cs ;copy CS to DS 3C56 1F pop ds 3C57 89 0E 30 3E mov [port],cx ;save port number 3C5B BA F8 03 mov dx,_BASE1 ;primary data port 3C5E 0B C9 or cx,cx ;COM1 ? 3C60 74 08 jz setadd ;jump if so 3C62 BA F8 02 mov dx,_BASE2 ;alternate data port 3C65 83 F9 01 cmp cx,1 ;COM2 ? 3C68 75 0D jnz badinit ;error if not 3C6A .setadd 3C6A 89 16 2C 3E mov [data],dx ;save base address 3C6E EC in al,dx ;clear any garbage 3C6F 83 C2 02 add dx,_INTID ;point to int. ID register 3C72 EC in al,dx ;and read register 3C73 A8 F8 test al,&F8 ;check card is present 3C75 74 04 jz setint ;yes, proceed 3C77 .badinit 3C77 33 C0 xor ax,ax ;set error return 3C79 99 cwd 3C7A CB retf ;and exit 3C7B ; 3C7B .setint 3C7B 53 push bx ;save baud rate 3C7C B8 0C 35 mov ax,&350C ;DOS read vector function 3C7F 2B C1 sub ax,cx ;adjust for this port 3C81 CD 21 int _MSDOS ;read old interrupt vector 3C83 89 1E 22 3E mov [oldcomi],bx ;save old interrupt vector 3C87 8C 06 24 3E mov [oldcomi+2],es 3C8B B8 0C 25 mov ax,&250C ;DOS write vector function 3C8E 2B C1 sub ax,cx ;adjust for this port 3C90 BA CA 3D mov dx,isr ;interrupt service routine 3C93 CD 21 int _MSDOS ;write new interrupt vector 3C95 5B pop bx ;restore baud rate 3C96 B4 10 mov ah,_IMASK ;IBM interrupt mask 3C98 D2 EC shr ah,cl ;position for this port 3C9A F6 D4 not ah ;and invert it 3C9C E4 21 in al,_MASKR ;get interrupt mask 3C9E 22 C4 and al,ah ;enable COM interrupt 3CA0 E6 21 out _MASKR,al ;put interrupt mask 3CA2 C7 06 26 3E 33 3E mov word ptr [rdptr],rxbuf 3CA8 C7 06 28 3E 33 3E mov word ptr [wrptr],rxbuf 3CAE C7 06 2A 3E 23 40 mov word ptr [limit],bufend-16 3CB4 C6 06 32 3E 00 mov byte ptr [error],0 ;clear error code 3CB9 ; 3CB9 .setcrd 3CB9 8B 0E 2C 3E mov cx,[data] ;reload base address 3CBD 8B D1 mov dx,cx ;get base address 3CBF 83 C2 03 add dx,_LINECTL ;add offset to get latch enable 3CC2 B0 83 mov al,&83 ;enable word 3CC4 EE out dx,al ;and enable latch 3CC5 BA 01 00 mov dx,(1.8432E6/16) DIV 65536 3CC8 B8 00 C2 mov ax,(1.8432E6/16) MOD 65536 3CCB F7 F3 div bx ;compute baud rate divisor 3CCD 8B D1 mov dx,cx ;get base address 3CCF EE out dx,al ;setup com port to given baud rate 3CD0 8A C4 mov al,ah 3CD2 42 inc dx ;second byte of divisor latch 3CD3 EE out dx,al 3CD4 4A dec dx ;get base address 3CD5 83 C2 03 add dx,_LINECTL ;line control register 3CD8 B0 03 mov al,&03 ;set 8 data, 1 stop, no parity 3CDA EE out dx,al 3CDB 42 inc dx ;modem control register 3CDC B0 0B mov al,&0B ;turn on DTR, RTS and IE 3CDE EE out dx,al 3CDF 42 inc dx ;line status register 3CE0 89 16 2E 3E mov [stat],dx ;save status port address 3CE4 EC in al,dx ;and clear 3CE5 42 inc dx ;modem status register 3CE6 EC in al,dx ;and clear it 3CE7 8B D1 mov dx,cx ;get base address 3CE9 42 inc dx ;interrupt enable register 3CEA B0 05 mov al,&05 3CEC EE out dx,al ;enable interrupts 3CED 42 inc dx ;interrupt ID register 3CEE B8 01 00 mov ax,1 3CF1 EE out dx,al ;initialise ID register 3CF2 99 cwd 3CF3 CB retf 3CF4 ; 3CF4 ;*************************************************************** 3CF4 ; 3CF4 ;Disable commumications interrupt and restore interrupt vector. 3CF4 ; 3CF4 ;Parameters 3CF4 ;None 3CF4 ;Returns 3CF4 ;None 3CF4 ; 3CF4 ._com_exit 3CF4 0E push cs ;copy CS to DS 3CF5 1F pop ds 3CF6 8B 16 2C 3E mov dx,[data] ;data register address 3CFA 42 inc dx ;interrupt enable register 3CFB 32 C0 xor al,al 3CFD EE out dx,al ;disable interrupts 3CFE 8B 16 2E 3E mov dx,[stat] ;status register address 3D02 4A dec dx ;modem control register 3D03 EE out dx,al ;turn off DTR, RTS, IE 3D04 8B 0E 30 3E mov cx,[port] ;com port number (0 or 1) 3D08 B4 10 mov ah,_IMASK ;IBM interrupt mask 3D0A D2 EC shr ah,cl ;position for this port 3D0C E4 21 in al,_MASKR ;get interrupt mask 3D0E 0A C4 or al,ah ;disable COM interrupt 3D10 E6 21 out _MASKR,al ;put interrupt mask 3D12 B8 0C 25 mov ax,&250C ;DOS write vector function 3D15 2B C1 sub ax,cx ;set for appropriate port 3D17 C5 16 22 3E lds dx,[oldcomi] ;get old vector 3D1B CD 21 int _MSDOS ;restore old vector 3D1D CB retf 3D1E ; 3D1E ;*************************************************************** 3D1E ; 3D1E ;Return the receive status from the serial port. 3D1E ; 3D1E ;Parameters 3D1E ;None 3D1E ;Returns 3D1E ;1 if data ready, 0 if no data 3D1E ; 3D1E ._com_stat 3D1E 2E A1 28 3E mov ax,cs:[wrptr] ;get buffer write pointer 3D22 2E 2B 06 26 3E sub ax,cs:[rdptr] ;same as read pointer ? 3D27 74 03 jz statex ;yes, so buffer is empty 3D29 B8 01 00 mov ax,1 ;no, so flag data ready 3D2C .statex 3D2C 99 cwd 3D2D CB retf 3D2E ; 3D2E ;*************************************************************** 3D2E ; 3D2E ;Output a byte to the serial port. 3D2E ; 3D2E ;Parameters 3D2E ;Data to send to port 3D2E ;Returns 3D2E ;Byte sent to port, or -1 if timeout 3D2E ; 3D2E ._com_put 3D2E 8B D8 mov bx,ax ;save byte 3D30 B9 FF FF mov cx,_DELAY ;Init timeout register 3D33 .stat0 3D33 2E 8B 16 2E 3E mov dx,cs:[stat] ;Get status port address 3D38 EC in al,dx 3D39 A8 20 test al,&20 ;ready yet ? 3D3B 74 06 jz stat1 ;no, keep waiting 3D3D 42 inc dx ;modem status register 3D3E EC in al,dx 3D3F A8 10 test al,&10 ;CTS asserted ? 3D41 75 07 jnz stat2 3D43 .stat1 3D43 E2 EE loop stat0 ;else wait 3D45 B8 FF FF mov ax,-1 ;set return code 3D48 99 cwd 3D49 CB retf ;and exit 3D4A ; 3D4A .stat2 3D4A 2E 8B 16 2C 3E mov dx,cs:[data] ;Get data port address 3D4F EC in al,dx ;get last received byte 3D50 3C 13 cmp al,_XOFF ;was it X-OFF ? 3D52 74 EF jz stat1 ;yes, so keep waiting 3D54 8B C3 mov ax,bx ;get byte 3D56 EE out dx,al ;output the data 3D57 99 cwd 3D58 CB retf 3D59 ; 3D59 ;*************************************************************** 3D59 ; 3D59 ;Read a byte from the receive buffer. 3D59 ; 3D59 ;Parameters 3D59 ;None 3D59 ;Returns 3D59 ;If ax is positive, al=byte read 3D59 ;If ax is negative, al=error code 3D59 ; 3D59 ._com_get 3D59 0E push cs ;copy CS to DS 3D5A 1F pop ds 3D5B A0 32 3E mov al,[error] ;get last error code 3D5E 0A C0 or al,al ;was there an error ? 3D60 75 4F jnz error_exit ;yes, so report it 3D62 8B 1E 26 3E mov bx,[rdptr] ;get buffer read pointer 3D66 3B 1E 28 3E cmp bx,[wrptr] ;buffer empty ? 3D6A 74 31 jz empty ;yes, so wait 3D6C 8A 07 mov al,[bx] ;get data byte from buffer 3D6E 43 inc bx ;bump buffer pointer 3D6F 81 FB 33 40 cmp bx,bufend ;past end of buffer ? 3D73 75 03 jnz notend ;no, so OK 3D75 BB 33 3E mov bx,rxbuf ;yes, so reset to start 3D78 .notend 3D78 89 1E 26 3E mov [rdptr],bx ;update read pointer 3D7C 3B 1E 28 3E cmp bx,[wrptr] ;buffer now empty ? 3D80 75 05 jnz notmt 3D82 50 push ax ;save data 3D83 E8 34 00 call x_on ;enable receive data 3D86 58 pop ax ;restore data 3D87 .notmt 3D87 8B 1E 2A 3E mov bx,[limit] ;get x-off limit 3D8B 43 inc bx ;bump it 3D8C 81 FB 33 40 cmp bx,bufend ;past end of buffer ? 3D90 75 03 jnz nottop ;no, so OK 3D92 BB 33 3E mov bx,rxbuf ;yes, so reset to start 3D95 .nottop 3D95 89 1E 2A 3E mov [limit],bx ;update x-off limit 3D99 32 E4 xor ah,ah ;make positive 3D9B 99 cwd 3D9C CB retf 3D9D ; 3D9D .empty 3D9D B9 FF FF mov cx,_DELAY 3DA0 .wait 3DA0 8B 1E 26 3E mov bx,[rdptr] ;get buffer read pointer 3DA4 3B 1E 28 3E cmp bx,[wrptr] ;buffer empty 3DA8 75 AF jnz _com_get ;no, so stop waiting 3DAA E2 F4 loop wait 3DAC E8 0B 00 call x_on ;enable receive data 3DAF B0 80 mov al,&80 ;time-out error code 3DB1 .error_exit 3DB1 C6 06 32 3E 00 mov byte ptr [error],0 ;clear error code 3DB6 B4 FF mov ah,-1 ;make negative 3DB8 99 cwd 3DB9 CB retf 3DBA ; 3DBA .x_on 3DBA 8B 16 2E 3E mov dx,[stat] ;get status register addr 3DBE 4A dec dx ;modem control register 3DBF B0 0B mov al,&0B ;assert DTR & RTS 3DC1 EE out dx,al 3DC2 8B 16 2C 3E mov dx,[data] ;get data register address 3DC6 B0 11 mov al,_XON 3DC8 EE out dx,al ;transmit X-ON 3DC9 C3 ret 3DCA ; 3DCA ;*************************************************************** 3DCA ; 3DCA ;Communications interrupt 3DCA ; 3DCA .isr 3DCA 50 push ax ;save registers 3DCB 53 push bx 3DCC 52 push dx 3DCD 2E 8B 16 2E 3E mov dx,cs:[stat] ;get status port address 3DD2 EC in al,dx ;read status 3DD3 A8 1E test al,&1E ;receive errors ? 3DD5 75 45 jnz isr2 ;yes, report them 3DD7 A8 01 test al,&01 ;received data waiting ? 3DD9 74 37 jz isr1 ;no, so do nothing 3DDB 2E 8B 16 2C 3E mov dx,cs:[data] ;get data port address 3DE0 EC in al,dx ;read received data 3DE1 2E 8B 1E 28 3E mov bx,cs:[wrptr] ;get buffer write pointer 3DE6 2E 88 07 mov cs:[bx],al ;store data in buffer 3DE9 43 inc bx ;bump buffer pointer 3DEA 81 FB 33 40 cmp bx,bufend ;now past end of buffer ? 3DEE 75 03 jnz isr0 ;no, so OK 3DF0 BB 33 3E mov bx,rxbuf ;yes, so reposition at start 3DF3 .isr0 3DF3 2E 3B 1E 26 3E cmp bx,cs:[rdptr] ;is buffer full ? 3DF8 74 20 jz isr3 ;yes, so flag error 3DFA 2E 89 1E 28 3E mov cs:[wrptr],bx ;update write pointer 3DFF 2E 3B 1E 2A 3E cmp bx,cs:[limit] ;is buffer filling ? 3E04 75 0C jnz isr1 3E06 B0 13 mov al,_XOFF 3E08 EE out dx,al ;send X-OFF 3E09 2E 8B 16 2E 3E mov dx,cs:[stat] ;get status port address 3E0E 4A dec dx ;modem control register 3E0F B0 0A mov al,&0A ;de-assert DTR 3E11 EE out dx,al 3E12 .isr1 3E12 5A pop dx ;restore registers 3E13 5B pop bx 3E14 B0 20 mov al,_INTAK ;acknowledge interrupt 3E16 E6 20 out _INTAK,al 3E18 58 pop ax 3E19 CF iret 3E1A ; 3E1A .isr3 3E1A B0 01 mov al,&01 ;buffer full error 3E1C .isr2 3E1C 2E A2 32 3E mov cs:[error],al 3E20 EB F0 jmps isr1 3E22 ; 3E22 ;*************************************************************** 3E22 ; 3E22 00 00 00 00 .oldcomi dd 0 ;old interrupt vector 3E26 00 00 .rdptr dw 0 ;buffer read pointer 3E28 00 00 .wrptr dw 0 ;buffer write pointer 3E2A 00 00 .limit dw 0 ;limit when x-off sent 3E2C 00 00 .data dw 0 ;data port address 3E2E 00 00 .stat dw 0 ;status port address 3E30 00 00 .port dw 0 ;COM port number (0 or 1) 3E32 00 .error db 0 ;error code 3E33 .rxbuf ;receive buffer 3E33 ;