;COM/ASM - RS232 Driver Program TITLE '' ; LF EQU 10 CR EQU 13 ; *GET COPYCOM:3 ;Copyright message *GET SVCMAC:3 ;SVC Macro equivalents ; ORG 2400H ; BEGIN @@CKBRKC JR Z,BEGINA ;Continue if no BREAK LD HL,-1 RET ;Return with abort code ; BEGINA PUSH DE ;Save DCB address POP IX ; in index reg LD (CLDCB),DE ; and in driver header @@DSPLY HELLO$ ;Welcome the user ; ; Check if entry from SET command ; @@FLAGS ;IY => flag table base BIT 3,(IY+'C'-'A') ;System request? JP Z,VIASET ;"Install with Set ; ; Grab system dependent vectors ; PUSH IY ;Set DE to flag base POP DE LD HL,'K'-'A' ;KFLAG$ ADD HL,DE LD (KFLAG),HL ;Save keyboard flag locn LD HL,'S'-'A' ;SFLAG$ ADD HL,DE LD (SFLAG),HL ;Save system flag location LD HL,'W'-'A' ;WRINT$ ADD HL,DE IF @BLD631 LD (L2520),HL ;<631>Save int mask ENDIF LD (WRINT),HL ;Save int mask LD HL,10-44 ;INTVC$+10 ADD HL,DE ;Save for receive int LD (INTVC),HL ; vector ; ; Move @ICNFG vector into driver ; LD A,(IY+28) ;Get current opcode LD (LINK),A ;Save in driver LD L,(IY+29) ;Get current address LD H,(IY+30) ;Put in driver code LD (LINK+1),HL ; ; Check if driver alread resident ; LD DE,CL$ ;Check if driver is @@GTMOD ; already resident EX DE,HL ;Put DCB ptr to HL JR NZ,NOTRES ;Go if not ; ; Make sure that the new DCB is same as the old ; LD C,(HL) ;P/u DCB pointer LSB INC HL LD B,(HL) ;P/u DCB pointer MSB LD HL,6 ;Get old DCB name & ADD HL,BC ; stuff into error LD A,(HL) ; message in case INC L ; a different DCB LD H,(HL) ; is referenced LD L,A LD (DCBNAM$),HL ;Stuff message with spec LD HL,(CLDCB) ;P/u DCB existing DCB OR A ; pointer SBC HL,BC ;Same DCB pointer? JP NZ,DCBERR ;Can't install if diff JP ISRES NOTRES LD DE,'IK' @@GTDCB ;Locate low memory ptr JP NZ,IOERR ;Go if not found DEC L LD D,(HL) ;P/u pointer to DEC L ; start of free LD E,(HL) ; low core LD (LCPTR+1),HL ;Save ptr for later LD HL,CLEND-CLDVR-1 ADD HL,DE ;Start + driver length LD (SVEND+1),HL LD BC,1300H ;Max addr + 1 XOR A SBC HL,BC ;See if room low JR C,PUTLOW ; and install there if so ; ; Check if high memory available ; BIT 0,(IY+'C'-'A') ;Memory frozen? JP NZ,NOROOM ;Can't install if so LD HL,0 LD B,L ;Get HIGH$ @@HIGH$ LD (SVEND+1),HL ;Top of driver OR A LD BC,CLEND-CLDVR ; minus length SBC HL,BC LD B,0 PUSH HL @@HIGH$ ; is new HIGH$ POP HL INC HL ;Plus one is start LD (HCPTR),HL ;Save it LD HL,HCPTR ; and point to it LD (LCPTR+1),HL LD A,0FFH ;Flag himem used LD (HGHFLG),A ; ; Relocate internal references in driver ; PUTLOW PUSH IX LD IX,RELTAB ;Point to relocation tbl SVEND LD HL,$-$ ;Find distance to move LD (CLDVR+2),HL ;Set last byte used LD DE,CLEND-1 OR A ;Clear carry flag SBC HL,DE LD B,H ;Move to BC LD C,L LD A,TABLEN ;Get table length RLOOP LD L,(IX) ;Get address to change LD H,(IX+1) LD E,(HL) ;P/U address INC HL LD D,(HL) EX DE,HL ;Offset it ADD HL,BC EX DE,HL LD (HL),D ;Put it back DEC HL LD (HL),E INC IX INC IX DEC A JR NZ,RLOOP ;Loop till done POP IX ;Restore DCB ; ; Set up @ICNFG ; LD HL,INIT ;Get (relocated) RX01 EQU $-2 LD (IY+29),L ; init address & put LD (IY+30),H ; into system ICNFG area LD A,0C3H ;Get JP instruction LD (IY+28),A ;Turn on ICNFG ; ; Move driver ; LCPTR LD HL,$-$ ;Low core or himem pointer LD E,(HL) INC L LD D,(HL) PUSH DE ;Save start LD HL,CLDVR LD BC,CLEND-CLDVR ;Calc driver length LDIR ;Move into place LD HL,(LCPTR+1) ;If driver went low, LD (HL),E ; need to update new INC L ; driver zone pointer LD (HL),D ; ; Initialize the driver ; LD HL,LINK ;Pt to ICFG link RX20 EQU $-2 LD E,(HL) ;Save what's there LD (HL),0C9H ;Make a ret. PUSH HL DI IF @BLD631 RX02: EQU $+1 ;<631> LD HL,RECVINT ;<631> INTVC: EQU $+1 ;<631> LD (0000H),HL ;<631> L2520: EQU $+1 ;<631> LD HL,0000H ;<631>Get interrupt mask SET 5,(HL) ;<631> ENDIF CALL INIT ;Init the UART RX11 EQU $-2 EI POP HL LD (HL),E ;Restore icnfg chain ; POP DE ;Pop filter start ; ISRES LD HL,CLACT$ ;Advise COM/DVR installed LD (IX),7 ;Init DCB type to "C/P/G" LD (IX+1),E ; & stuff the driver LD (IX+2),D ; address @@LOGOT LD A,$-$ ;Did it use high memory? HGHFLG EQU $-1 OR A ;NZ if high JR Z,NTHGH LD HL,HMEM$ ;"Driver in himem... @@LOGOT NTHGH LD HL,0 ;Init on error code RET ; and exit ; ; Error exits ; VIASET LD HL,VIASET$ ;"Install with Set DB 0DDH DCBERR LD HL,DCBERR$ ;"Driver being used already DB 0DDH NOROOM LD HL,NOROOM$ ;"Memory frozen @@LOGOT LD HL,-1 ;Set abort code @@CKBRKC ;Clear any break RET ; IOERR LD L,A ;Error code to HL LD H,0 OR 0C0H ;Set short,return LD C,A ;Error to C @@ERROR ; for error dsply @@CKBRKC ;Clear any break RET ; ; Messages & Data tables ; HCPTR DW 0 ;Save start if going to HIGH$ CL$ DB '$CL',3 HELLO$ DB 'RS-232 Driver' ; *GET CLIENT:3 ; NOROOM$ DB 'No memory space available',CR DCBERR$ DB 'Driver already attached to *xx',CR DCBNAM$ EQU $-3 CLACT$ DB 'COM driver is now resident',CR VIASET$ DB 'Must install via SET',CR HMEM$ DB LF,'Note: driver installed in high memory',CR ; @WRINT EQU 0E0H WRINT$ EQU 80H ; MASRES EQU 0E8H ;RS232 ports MODSTAT EQU 0E8H BAUDSET EQU 0E9H UARTCTL EQU 0EAH UARTST EQU 0EAH DATAREG EQU 0EBH ; ; Actual driver ; CLDVR EQU $ JR CLBGN ;Branch around linkage DW CLEND ;Last byte used DB 3,'$CL' CLDCB DW $-$ DW 0 CLDATA$ EQU $ MSMASK EQU $-CLDATA$ DB 0 ; ; UART control port image ; ; bit 7: 1 = even parity, 0 = odd parity ; bits 6,5: word length <00=5, 10=6, 01=7, 11=8> ; bit 4: 1 = 2 stop bits, 0 = 1 stop bit ; bit 3: 1 = disable parity, 0 = enable parity ; bit 2: 1 = enable transmit data, 0 = break ; bit 1: 0 = Data Terminal Ready ; bit 0: 0 = Request to Send ; UCIMAGE EQU $-CLDATA$ DB 0A5H BAUDRT DB 55H ;Init 300 baud LOGBRK EQU $-CLDATA$ DB 3 ;Default is Control-C CLFLG EQU $-CLDATA$ DB 0 ;Init no char in buf CLBUF EQU $-CLDATA$ DB 0 ;One-char buffer ; ; CL initialization routine. Set up DR interrupt ; vector & initialize the hardware ; IF @BLD631 WRINT: EQU $+1 ;<631> INIT LD A,(0000H) ;<631>Interrupt enable mask ELSE INIT LD HL,RECVINT ;Vector address RX02 EQU $-2 LD ($-$),HL ;INTVC$+10 INTVC EQU $-2 LD HL,WRINT$ ;Interrupt enable mask WRINT EQU $-2 SET 5,(HL) ;Enable RS232 DR LD A,(HL) ENDIF OUT (@WRINT),A CALL CTL2 ;Init the hardware RX03 EQU $-2 LINK RET ;Link back thru any DB 0,0 ; existing ICNFG ; ; Initialize the UART & BRG ; CTL2 LD BC,(CLDATA$+UCIMAGE) ;P/u values from DCB RX04 EQU $-2 OUT (MASRES),A ;Reprime UART LD A,C OUT (UARTCTL),A LD A,B OUT (BAUDSET),A RET ; CLBGN LD IX,CLDATA$ ;Point to data area RX05 EQU $-2 JR C,RECV ;Go if @GET request JR Z,SEND ;Go if @PUT request LD A,C ;P/U @CTL byte OR A ;@CTL 00 ? JR Z,CANISND ;Go if so DEC A ;@CTL 01 ? JR Z,CTL1 ;Go if so DEC A ;Was it CTL-2 "INIT UART" JR Z,CTL2 ;Go if so CP 4-2 ;Wakeup feature? JR Z,CTL4 ;Go if wakeup feature XOR A RET ; CTL4 PUSH IY ;Transfer pointer to HL IF @BLD631 POP DE ;<631> LD A,D ;<631>Test if set or reset OR E ;<631> LD A,0C9H ;<631>Init disable wakeup RX06: EQU $+1 ;<631> LD HL,(WAKEADR+1) ;<631>P/u old address ELSE POP HL LD A,H ;Test if set or reset OR L LD A,0C9H ;Init disable wakeup EX DE,HL ;Switch new value to DE LD HL,(WAKEADR+1) ; & p/u old in HL RX06 EQU $-2 ENDIF JR Z,SETWAK ;Jump if disable LD A,0C3H ;Make enable SETWAK LD (WAKEADR),A ;Load the opcode RX07 EQU $-2 LD (WAKEADR+1),DE ;Then the address RX08 EQU $-2 PUSH HL ;Transfer pointer to IY POP IY RET ; ; Check if ready to send ; CANISND IN A,(UARTST) ;Look at TX empty bit CPL ;Flip it AND 40H ;Mask out all else IN A,(MODSTAT) ;P/U modem status reg RET NZ ;Return if can't send LD B,A ;Save modem status reg XOR (IX+MSMASK) ;Mask for which to flip RRA ;Move into bits 0-3 RRA RRA RRA AND (IX+MSMASK) ;Mask for which to check AND 0FH ;Mask off garbage LD A,B ;Get back reg RET ;Ret with Z or NZ ; ; Send character ; SEND LD A,(IX+UCIMAGE) ;Get UART ctrl reg OUT (UARTCTL),A ;Put it (clears BREAK) SWAIT CALL CANISND ;Poll RX09 EQU $-2 JR NZ,SWAIT ; until ready LD A,C ;Get byte to send OUT (DATAREG),A ;Send it with Z-flag RET ; unchanged for return ; ; Receive character - Get from buffer if available ; RECV1 CALL CKINP ;Ck if avail from port RX10 EQU $-2 RET NZ ;Back if none RECV SLA (IX+CLFLG) ;Ck if avail from buf JR NC,RECV1 ;Go if none avail LD A,(IX+CLBUF) ;Get the char CP A ;Set Z-flag & exit RET ; ; Break request ; CTL1 LD A,(IX+UCIMAGE) ;Pick up UART ctl image RES 2,A ;Show BREAK bit OUT (UARTCTL),A RET ;With Z-flag ; ; Data received interrupt handler ; RECVINT LD IX,CLDATA$ ;Base of data area RX13 EQU $-2 CALL CKINP ;See if available from port RX12 EQU $-2 LD A,B WAKEADR RET ;Wakeup if enabled DW 0 ;Space for address ; ; Routine to check on a received character ; CKINP IN A,(UARTST) ;Check if actually RX LD B,A ;Save status IF @BLD631 CPL ;<631>Invert bit 7 AND 80H ;<631>Set NZ if none avail ELSE AND 80H ;Mask Data Received bit XOR 80H ;Set NZ if none avail ENDIF LD A,0 ;Set "No error" RET NZ ;Return if none IN A,(DATAREG) ;Pick up character LD C,A ;Save tempy in reg-C ; ; Break, Pause & Enter handler routine ; LD HL,$-$ ;KFLAG$ KFLAG EQU $-2 CP CR ;ENTER char received? JR NZ,PAWSCK ;Go if not SET 2,(HL) ;Set ENTER bit JR RECVEX ; PAWSCK CP 60H ;Pause char received? JR NZ,BRKCHK ;Go if not SET 1,(HL) ;Set pause bit JR RECVEX ; BRKCHK LD A,(IX+LOGBRK) ;Break char received? OR A ;Check if LOGBRK=0 JR Z,RECVEX ;No valid break if =0 CP C ;Check if a valid BREAK JR Z,BRKRECD ;Go if so IN A,(UARTST) ;Check for framing error IF @BLD631 AND 10H ;<631> ELSE BIT 4,A ENDIF JR Z,RECVEX ;Quit if none ; ; A BREAK was received, ck system's BREAK disable ; BRKRECD LD A,($-$) ;Check if break key SFLAG EQU $-2 AND 10H ; is disabled LD A,0 ;Return NZ & A=0 if RET NZ ; the BREAK is disabled SET 0,(HL) ;Else set break bit LD C,80H ; & reset BREAK code RECVEX LD (IX+CLBUF),C ;Put char into 1-char buf LD (IX+CLFLG),80H ; & set char available XOR A ;Set Z flag RET CLEND EQU $ ; IF @BLD631 RELTAB DW RX01,RX02,RX03,RX04,RX05,RX06,RX07,RX08 ;<631> ELSE RELTAB DW RX01,RX02,RX03,RX04,RX05,RX06,RX07,RX08 ENDIF DW RX09,RX10,RX11,RX12,RX13,RX20 TABLEN EQU $-RELTAB/2 ; END BEGIN