; ; KISS TNC FOR THE TNC-2 AND CLONES ; ; K3MC 30 SEP 86 - ORIGINAL VERSION ; ; 1 MAR 87. FIXED ALL KNOWN BUGS. RE-ARRANGE CODE TO ALLOW ROMING (THIS ; MEANS THAT DATA AREAS NEED TO BE INITIALIZED FROM THE CODE). FIGURE OUT THE ; STACK POINTER GIVEN THE AMOUNT OF AVAILABLE RAM. INCLUDE THE CODES 05 00 ; AND 05 01 TO MEAN FULL DUPLEX OFF AND FULL DUPLEX ON, RESPECTIVELY. ; CLEAR OUT ALL AVAILABLE RAM. DO A "DANCE" WITH LEDS WHEN INITIALLY BOOTED: ; FLASH THE LED(S) FOR ABOUT 5 SECONDS SUCH THAT CON ONLY FLASHES IF YOU HAVE ; 8K RAM, STA ONLY FLASHES IF 16K RAM, AND STA AND CON FLASH IF 32K RAM. ; ; 29 MAR 87. ADD CODE TO DISCARD BREAK CHARS, AND CHARS WITH FRAMING ERRORS. ; FIX BUG IN IB_RCA WHICH DID NOT DISCARD NULL RECEIVED FRAMES. ; Jan 89 ; ; Modified by John Wiseman, G8BPQ for use with ZILOG assembler, ; and to reset on error conditions (underrun or buffer overload) ; ; Feb 90 ; ; Further modifications by G8BPQ to provide a 'polled mode', ; to allow multidropping via a diode matrix (or similar). ; ; Address is included in upper nibble of command byte, and ; TNC only sends to host on receipt of a Poll (FEND ?D). ; A checksum is also added as protection against corruption ; on AX25. (We dont have the benefit of the IP Checksum!) ; ; ; June 90 ; ; Provides an ACK when a frame has been completely transmitted, ; if frame type is 0ch. The first 2 bytes of the frame are not ; transmitted, but are returned as the ack. ; ; ; Buffering structure rewritten to be rather faster - and ; hopefully more stable, but now max frame size is 340 bytes. ; This is fine for use with AX.25, but maybe not with IP. ; ; Poll changed to 0Eh, and checksum put before FEND, for ; compatibility with Kantronics KISS ROMS ; ; ; 1 Jan 1992 ; ; CWID timer initialised from CHANNEL, to stop all TNCs ; keying at once. ;PROM EQU 0 ; DOWN LINE LOAD PROM EQU 1 ; PROM VERSION COND PROM-1 ORG 8000H ; FOR DOWN LINE LOAD ENDC FALSE EQU 0 TRUE EQU 0FFH I_REGISTER EQU $/256 SIO EQU 0DCH ;ACTUALLY, ONLY A5 IS USED FOR SIO -CS A_DAT EQU SIO+0 ;MODEM PORT A_CTL EQU SIO+1 ;MODEM PORT B_DAT EQU SIO+2 ;USER SERIAL PORT B_CTL EQU SIO+3 ;USER SERIAL PORT DCD EQU 8 ;BIT IN RR0, USED IN CH A TBE EQU 4 ;TX BUFFER EMPTY BIT RTS EQU 2 ;REQUEST TO SEND (PTT BIT IN WR5 OF CHAN A) FRAMING_ERROR EQU 40H ;BIT IN RR1 FOR ASYNC FRAMING ERROR BREAK_ABORT EQU 80H ;BIT IN RR0 FOR ASYNC BREAK DETECTION FEND EQU 300O ;300 OCTAL FESC EQU 333O ;333 OCTAL TFEND EQU 334O ;334 OCTAL TFESC EQU 335O ;335 OCTAL ALEDON EQU 69H ;BITS FOR WR5 TO TURN ON STA LED ALEDOFF EQU 0E9H ;BITS FOR WR5 TO TURN OFF STA LED ALED EQU 80H ;THE DTR BIT IN CH A WR5, WE WILL SOON REMOVE ;PREVIOUS 2 DEFINITIONS & USE THE MEMORY LOC. ;A_WR5 TO HOLD CH A WR5'S VALUE, BECAUSE WE ;NEED TO BE AWARE WHEN WE ARE TRANSMITTING! BLEDON EQU 6AH ;BITS FOR WR5 TO TURN ON CON LED BLEDOFF EQU 0EAH ;BITS FOR WR5 TO TURN OFF CON LED BLED EQU 80H N_EVENTS EQU 4 ; SO FAR, ONLY 3 REAL-TIME EVENTS ; + CLOCK OFR CWID START: JP CODE_START ;GO AROUND THIS DATA AREA VERSION: DEFM 'BPQKISS Jan92' ;13 BYTES (EXACTLY!) HERE FOR VERSION STRING DEFW IB_TBE ;CH B TRANSMITTER BUFFER EMPTY INTERRUPT/USER DEFW IB_EXT ;CH B EXT/STATUS CHANGE/USER DEFW IB_RCA ;CH B RECEIVED CHAR AVAILABLE/USER DEFW IB_SPECIAL ;CH B SPECIAL RECEIVE CONDITION/USER DEFW IA_TBE ;CH A TRANSMITTER BUFFER EMPTY INTERRUPT/MODEM DEFW IA_EXT ;CH A EXT/STATUS CHANGE/MODEM DEFW IA_RCA ;CH A RECEIVED CHAR AVAILABLE/MODEM DEFW IA_SPECIAL ;CH A SPECIAL RECEIVE CONDITION/MODEM CHANNEL DEFB 00H ; HERE TO SIMPLIFY PATCHING CWID DEFS 512 ; PATCH ID IN HERE - 20 BYTES PER SECOND, ; SO ENOUGH ROOM FOR 25 SECS OF ID! MODEFLAG DEFB 1H POLLMODE EQU 0 A_INIT: DEFB 18H,4,20H,1,1BH,7,7EH,5,0E9H,3,0C9H ;FOR MODEM A_SIZE EQU $-A_INIT B_INIT: DEFB 18H,4,44H,2,10H,3,0C1H,5,0EAH,1,1FH ;FOR TTY B_SIZE EQU $-B_INIT ;*************************************************************************** CODE_START: DI ;NO INTERRUPTS FOR THE MOMENT... ;INIT SIO. THIS IS REQUIRED EVEN IF WE WANNA FLASH LEDS... IN A,(A_CTL) ;ASSURE WE ARE TALKING TO CH 0 LD C,A_CTL LD B,A_SIZE LD HL,A_INIT OTIR ;INIT SYNC (MODEM) PORT ;INIT ASYNC PORT, ALSO TO ALLOW FLASHING LEDS IN A,(B_CTL) ;ASSURE WE ARE TALKING TO CH 0 LD C,B_CTL LD B,B_SIZE LD HL,B_INIT OTIR ;INIT ASYNC PORT & INTERRUPT VECTOR ; ; FIGURE OUT WHERE TOP OF STACK IS, SET STACK POINTER. ; SILLY TNC-2 DOES NOT DO COMPLETE ADDRESS DECODING FOR THE RAMS IF YOU ARE ; USING ONLY THE TWO 8K X 8 CHIPS. HACK TO FIGURE OUT TOP OF MEMORY SO WE CAN ; SET STACK POINTER. NEWER HACK TO SEE IF WE'VE ONLY GOT 8K RAM. LD A,(9FFFH) ;TOP OF RAM IF ONLY 8K CPL LD B,A LD (9FFFH),A ;WRITE ONE'S COMPLEMENT INTO MEM LD A,(9FFFH) CP B ;SEE IF IT "TOOK" JP Z,OK_8 ;YES, WE HAVE AT LEAST 8K OF RAM HALT ;ELSE THERE IS NO RAM, SO STOP OK_8: LD A,(0BFFFH) ;TOP OF RAM IF 16K CPL LD B,A LD (0BFFFH),A ;SAME ONE'S COMPLEMENT HACK LD A,(0BFFFH) CP B JP Z,OK_16 ;WE HAVE AT LEAST 16K OF RAM LD SP,0A000H LD C,BUFF8K ; NUMBER OF BUFFERS LD D,0FFH ;BLINK CON LED LD E,0 ;BUT NOT STA LED (I.E., WE HAVE 8K) JP STACK_LOADED ;ELSE WE ONLY HAVE 8K OF RAM ;BECAUSE PREVIOUS COMPARE FAILED ;HERE IF WE'VE GOT AT LEAST 16K RAM OK_16: LD A,55H ;ONE VALUE LD (0BFFFH),A LD A,0AAH LD (0FFFFH),A ;OTHER VALUE LD A,(0BFFFH) ;GET WHAT SHOULD BE 55H IF 32K CP 55H LD SP,0 LD C,BUFF32K LD DE,0FFFFH ;BLINK BOTH CON AND STA LEDS (IF 32K) JR Z,STACK_LOADED ;IF IS 55H, THEN WE'VE GOT 32 K, ELSE 16 K LD SP,0C000H ;FORCE STACK VALUE. LD C,BUFF16K LD D,0 ;DO NOT BLINK CON LED IF 16K RAM STACK_LOADED: PUSH DE ;DE HAS LOGICAL VALUES WHICH TELL US WHICH ;LEDS TO FLASH (WHICH WE DO LATER...) PUSH BC ; C HAS NUMBER OF BUFFERS EXX POP BC ; AND NO OF BUFFERS POP HL ;TEMP. STORE THIS INFO IN OTHER REG SET EXX ;CLEAR OUT RAM. LD HL,0 ADD HL,SP ;NOW HL HAS VALUE OF SP (THAT IS, TOP OF ;MEMORY + 1) DEC HL ;NOW HL HAS TOP OF MEMORY ADDRESS LD DE,FREE_RAM ;GET START OF AVAILABLE FREE RAM XOR A ;CLEAR CARRY AND SET A TO 0 LD (DE),A ;FIRST FREE RAM LOCATION IS ZEROED.... SBC HL,DE ;GET INTO HL # OF BYTES OF FREE RAM. IF WE ;ARE IN ROM, THEN ALL RAM IS FREE, ELSE IF WE ;ARE RUNNING FROM RAM, THE CODE PART IS NOT ;FREE, AND THIS COMPENSATES FOR THIS. DEC HL ;ONE FEWER BYTES FOR NUMBER TO MOVE... LD B,H LD C,L ;GET BYTE COUNT INTO BC LD H,D LD L,E ;GET "SOURCE" ADDRESS = FREE_RAM INC DE ;SET "DESTINATION" ADDRESS = FREE_RAM + 1 LDIR ;ZERO MEMORY. ; ; SET UP NON-ZERO INITIAL VALUES ; LD A,0 LD (IN_STATE),A ;CONVERT BACK TO 1 IN V.32 CODE ;INPUT STATE MACHINE STATE ;4 MAR 8: MAKE IT 0 (FROM 1) BECUZ ;NOISE ON LINE IS FIRST TRIGGERING THE ;CODE TO ASSUME THAT A FRAME IS COMING ;FROM THE HOST..... COMMENT BELOW WAS ;APPROPRIATE BEFORE ;ASSUME THAT WE'VE SEEN AN FEND FROM ;(NON-EXISTENT) "PREVIOUS" FRAME. THIS ;MEANS THAT WHEN WE ARE RECEIVING DATA ;FROM USER, THERE NEED BE ONLY THE ;FEND CHAR AT THE END OF A FRAME, AND ;NOT AT THE BEGINNING (ALTHOUGH IF A ;FEND IS AT THE BEGINNING, IT IS ;IGNORED.) LD A,ALEDOFF LD (A_WR5),A ;STATE OF STA LED & RTS (PTT) LINE, ;MAINLY... (FOR CH A ONLY [MODEM] ) LD A,BLEDOFF LD (B_WR5),A ; ; SET UP BUFFER POOL ; EXX LD A,C ; BUFFERS EXX LD B,A LD DE,BUFFERS BUFLOOP: LD HL,FREE_Q CALL Q_ADDF LD HL,BUFFLEN ADD HL,DE LD D,H LD E,L DJNZ BUFLOOP ;INIT REGS FOR IB_EXT INTERRUPT EXX LD BC,0 ;SET PREV STATE OF SYNC PIN,FOR 1200HZ LD DE,0 ;COUNT OF # OF INTERRUPTS INIT EXX LD HL,TXQ_ENABLES LD B,N_EVENTS XOR A E_CLEAR: LD (HL),A ; TURN OFF ALL THE ENABLES OF ALL ... INC HL ; ... POSSIBLE EVENTS. DJNZ E_CLEAR ;INIT THE ROUTINE ADDRESSES IN OUR EVENT TABLE LD HL,R_DELAY LD (TXQ_ADDRESSES),HL LD HL,R_SLOTTIME LD (TXQ_ADDRESSES+2),HL LD HL,R_TAIL LD (TXQ_ADDRESSES+4),HL LD HL,RTCLOCK LD (TXQ_ADDRESSES+6),HL ; REAL TIME CLOCK FOR CWID ; LD A,(CWID) CP 0FFH JR Z,SKIPID ; NO CWID DEFINED ; ; ENABLE RTC FOR CWID ; LD HL,6000 ; MINUTE IN 1/100THS OF SEC LD (TXQT_CWID),HL ; GET TIMER VALUE INTO TIMER SLOT LD A,1 LD (TXQE_CWID),A ; ENABLE THIS EVENT LD A,(CHANNEL) RRCA RRCA RRCA RRCA AND 0FH INC A LD (MINUTES),A ; RANDOMISE FIRST CWID SKIPID: LD A,50 LD (TXDELAY),A ; TX DELAY DEFAULT IS 500 MS LD A,64 LD (PERSISTENCE),A ; SET DEFAULT VALUE FOR PERSISTENCE LD A,10 LD (SLOTTIME),A ; AND SLOT TIME DEFAULTS TO 100 MS LD A,3 LD (TAILTIME),A ; TAIL TIMER DEFAULT XOR A LD (TX_Q),A ; NOW HAVE THE CON AND STA LEDS DO A "DANCE". EXX PUSH HL EXX POP DE ;WE SAVED THE LOGICALS TELLING US WHICH LEDS ;TO FLASH WHEN WE FIGURED OUT THE STACKSIZE. ;THIS IS HOW WE KNOW WHICH LEDS TO BLINK. LD B,6 ;DO IT 6 TIMES (ARBITRARY AS HELL, BUT SHOULD ;BE AN EVEN NUMBER SO THAT THE LEDS ARE OFF AT ;THE END OF THIS MESS...) LD HL,0 ;USE HL AS DOWNCOUNTER DANCE0: LD A,D OR A CALL NZ,CON_FLIP LD A,E OR A CALL NZ,STA_FLIP DANCE1: DEC HL LD A,H OR L JP NZ,DANCE1 DJNZ DANCE0 ;DO THIS 6 TIMES (3 "CYCLES") ;PREVIOUS STUFF SHOWED THAT THE DOWNLOAD OR BOOT WORKED PROPERLY... ;WE RE-INITIALIZE THE SIO PORTS SO THAT WE FLUSH GARBAGE CHARS THAT MAY HAVE ;COME IN WHILE WE WERE DIDDLING THE LEDS. THIS IS NECESSARY BECAUSE UNLESS WE ;DO THIS, THEN THE A CHANNEL (MODEM) GET RX OVERRUN (ESP IF TNC WAS LISTENING ;TO NOISE) AND RX OVERRUN IS VERY BAD - SO BAD, IN FACT, THAT I TURN ON BOTH ;CON AND STA AND HALT, BECAUSE THIS SITUATION SHOULD NEVER HAPPEN IN NORMAL ;USE. I FLUSH THE B (TTY) CHANNEL IN CASE ANYTHING WAS SENT TO IT IN MID- ;STREAM. ;RE-INIT SIO. IN A,(A_CTL) ;ASSURE WE ARE TALKING TO CH 0 LD C,A_CTL LD B,A_SIZE LD HL,A_INIT OTIR ;INIT SYNC (MODEM) PORT ;RE-INIT ASYNC PORT. IN A,(B_CTL) ;ASSURE WE ARE TALKING TO CH 0 LD C,B_CTL LD B,B_SIZE LD HL,B_INIT OTIR ;INIT ASYNC PORT & INTERRUPT VECTOR ; PREPARE TO LOAD HI BITS OF INTERRUPT VECTOR LD HL,BTIGNORE LD (BTXVEC),HL ; B TX NOT ACTIVE ; LD A,I_REGISTER LD I,A ;SET INTERRUPT PAGE FOR MODE 2 INTS IM 2 EI ;LET 'EM RIP! ;----------------------------------------------------------------------------- ; THIS IS THE BACKGROUND PROGRAM. ; NOTE THAT SINCE EVERYTHING ELSE IS INTERRUPT DRIVEN, AND SAVES REGISTERS, ; THIS PART OF THE CODE CAN USE REGISTERS & EXPECT VALUES TO STAY. COMMUTATOR_LOOP: ; ; SEE IF FINISHED SENDING A FRAME TO RADIO PORT ; LD A,(BGACT) OR A JP Z,NO_ACTION ; ; RELEASE CURRENT FRAME, GENERATING ACK IF NEEDED, THEN SEE IF MORE TO ; SEND ; XOR A LD (BGACT),A LD DE,(TX_BUF) INC DE INC DE INC DE INC DE LD A,(DE) ; FIRST BYTE AND 0FH ; MASK CHENNEL JP Z,NO_ACK ; NORMAL MODE FRAME ; LD HL,(TX_BUF) INC HL INC HL LD (HL),5 INC HL LD (HL),0 ; LENGTH INC HL INC HL INC HL ; SKIP ID BYTES INC HL LD (HL),FEND LD DE,(TX_BUF) LD HL,OUT_Q DI CALL Q_ADD EI JP TRYMORE NO_ACK: LD DE,(TX_BUF) LD HL,FREE_Q DI CALL Q_ADDF EI TRYMORE: ; ; IF MORE TO SEND, CHECK TAIL TIMER AND RTSG ; LD A,(TX_Q) OR A JP Z,NO_ACTION ; NOTHING TO SEND LD A,(TXQE_TAIL) OR A JP NZ,NO_ACTION ; TIAL IS RUNNING ?? COULD RETRIGGER ?? LD A,(A_WR5) AND RTS JP Z,NO_ACTION ; RTS DOWN ; ; MUST HAVE JUST FINISHED SENDING A FRAME - SEND NEXT CALL STARTFRAME ; SEND NEXT NO_ACTION: ; ; SEE IF ID NEEDED ; LD A,(CWIDFLAG) OR A JR NZ,TRY_ID LD A,(TX_Q) ; ANYTHING TO SEND? OR A JP Z,SCAN_CHECK ;...WORRY ABOUT TRANSMITTER TRY_ID: ; IF THERE ARE FRAMES TO TRANSMIT, WE MAY HAVE TURNED ON TXDELAY, OR WE MAY BE ; TRANSMITTING A FRAME SO CHECK FIRST. ; (THIS BUG FOUND LATE ON 30 SEP 86) THE CLEANEST WAY TO DO ; THIS IS TO CHECK IF WE ARE KEYED UP. IF SO, NOTHING ELSE TO DO FOR NOW ; HERE. THIS IS THE "LAST BUG!" FOUND AT 11:59PM EDT ON 30 SEP. LD A,(A_WR5) AND RTS JP NZ,SCAN_CHECK ;IF TX KEYED UP, NOTHING FOR US TO ;DO HERE! ; ELSE WE'VE NOTICED THAT WE'VE GOT SOME FRAME(S) TO SEND. ; TRY TO KEYUP TX LD A,(FULL_DUPLEX) OR A JP NZ,KEY_UP ;IF FULL DUPLEX, THEN THERE IS NO ;NEED TO WORRY ABOUT ALL THIS SILLY ;SLOT TIME AND PERSISTENCE STUFF! LD A,(TXQE_SLOTTIME) ;GET SLOTTIME TIMER ENABLE OR A JP NZ,SCAN_CHECK ;IF WE'RE WAITING, KEEP WAITING! ;CHECK IF CARRIER DETECT IS ACTIVE LD A,(DCD_STATE) ;DCD_STATE IS SET IN INTERRUPT ROUTINE OR A JP NZ,SCAN_CHECK ;IF CARRIER ACTIVE, WAIT IT OUT ;SO, DCD IS INACTIVE; DO PERSISTENCE ALGORITHM LD A,R ;GRAB THE Z-80 REFRESH REGISTER ADD A,A ;DOUBLE;NOW 0 <= A REG <= 254 LD B,A ;B HOLDS OUR "RANDOM" NUMBER LD A,(PERSISTENCE) SUB B ;A REG = PERSISTENCE - RANDOM # JP C,NO_PTT ;IF (P-R) < 0 THEN NO PTT NOW ; NOTE THAT P=255 MEANS ALWAYS KEY UP ;OK, SO WE'VE WON WITH THE RANDOM NUMBER GENERATOR. KEYUP TX AND START THE ;TXDELAY TIMER KEY_UP: ; ; SEE IF ID NEEDED ; LD A,(CWIDFLAG) OR A JP Z,NO_ID CALL STARTID JP SCAN_CHECK NO_ID: LD A,(TXDELAY) LD H,0 LD L,A ;HL IS 16-BIT VALUE OF TXDELAY LD (TXQT_DELAY),HL ;GET TIMER VALUE INTO TIMER SLOT LD A,1 LD (TXQE_DELAY),A ;ENABLE THIS EVENT LD A,5 DI ;WE NEED QUITE TIME HERE. OUT (A_CTL),A ;;;READY TO WRITE INTO WR5 OF CH A LD A,(A_WR5) OR RTS ;;;TURN ON THE PTT BIT... LD (A_WR5),A ;;;...IN THE MEMORY COPY OF WR5 OUT (A_CTL),A ;;; KEYUP TRANSMITTER EI JP SCAN_CHECK ;THAT'S ALL WE DO FOR NOW, WE AWAIT ;TXDELAY EVENT NO_PTT: ;SINCE WE LOST ON RANDOM #, WAIT SLOTTIME BEFORE TRYING AGAIN LD A,(SLOTTIME) LD H,0 LD L,A ;HL HAS 16-BIT VERSION OF SLOTTIME LD (TXQT_SLOTTIME),HL ;SET UP THE TIMER VALUE OF THIS EVENT LD A,1 LD (TXQE_SLOTTIME),A ;AND ENABLE THIS EVENT ; NOTE THAT THIS CODE DOES NOT HAVE TO BE INTERRUPT PROTECTED BECAUSE WE ; REALLY DON'T CARE IF THE SLOT TIMER IS DECREMENTED BETWEEN BEING LOADED ; AND BEING ENABLED. SCAN_CHECK: LD HL,TXQ_ENABLES ; GEAR UP TO CHECK TIMER ROUTINES LD IX,TXQ_TIMERS LD IY,TXQ_ADDRESSES LD DE,2 ;BUMP IX & IY BY TWOS LD B,N_EVENTS ;NUMBER OF POSSIBLE EVENTS SCAN_TOP: LD A,(HL) OR A JP Z,SCAN_BOTTOM ;IF NOT ENABLED, CHECK NEXT ONE ;ELSE IS ENABLED. TIMER EXPIRED? LD A,(IX+1) LD C,A ;SAVE MS BYTE FOR POSSIBLE USE LATER OR (IX) JR Z,SCAN_FIRE ;FIRE THIS IF WE ARE AT 0 COUNT LD A,C OR A ; SAVES US SOME TIME DOING IT THIS WAY JP P,SCAN_BOTTOM ; OR FIRE IF WE ARE NEGATIVE SCAN_FIRE: XOR A LD (HL),A ;DISABLE THIS EVENT AS IT FIRES PUSH HL LD HL,SCAN_RETURN ;LOAD UP ROUTINE RETURN ADDRESS PUSH HL ;SAVE AS RETURN ADDRESS ON STACK LD H,(IY+1) LD L,(IY) ;GET ADDRESS OF ROUTINE TO "CALL" JP (HL) ;"CALL" THIS ROUTINE SCAN_RETURN: ;WHERE ALL ROUTINES RETURN POP HL ;GET ORIGINAL HL BACK SCAN_BOTTOM: INC HL ;INCREMENT ENABLE TABLE POINTER ADD IX,DE ;KEEP TIMER TABLE POINTER IN STEP ADD IY,DE ;KEEP ROUTINE TABLE POINTER IN STEP DJNZ SCAN_TOP ;LOOK AT ALL ENTRIES IN TABLES ;NOW SEE IF WE NEED TO START AN OUTPUT TO RS-232 (HOST) PORT LD A,(OUT_STARTED) OR A ;ALSO CLEARS CARRY (SEE BELOW) JP NZ,COMMUTATOR_LOOP ;IF OUTPUT STARTED, NOTHING TO DO ; ELSE WE SHOULD CHECK TO SEE IF WE NEED TO START AN OUTPUT CALL CON_OFF ;;; LD HL,MODEFLAG BIT POLLMODE,(HL) JR Z,NOTPOLLMODE LD A,(POLLED) OR A JP Z,COMMUTATOR_LOOP ; NO POLL, SO CANT SEND ; XOR A LD (POLLED),A ; CLEAR ; ; IF DATA AVAIL, SEND IT, ELSE SEND ACK ; NOTPOLLMODE: LD HL,OUT_Q DI CALL Q_REM EI JR NZ,SENDIT ; GOT A FRAME LD HL,MODEFLAG BIT POLLMODE,(HL) ; IF POLLED MODE SEND ACK JP NZ,SEND_ACK JP COMMUTATOR_LOOP ;ELSE WE NEED TO START AN OUTPUT SENDIT: LD HL,B_TNORM LD (BTXVEC),HL ; NORMAL FRAME SEND LD (OUT_BUF),DE INC DE INC DE LD A,(DE) LD C,A INC DE LD A,(DE) LD B,A ; LENGTH TO BC INC DE LD A,(DE) AND 0FH ; MASK CHANNEL CP 0CH JP NZ,NOTRESP ; ; RESPONSE FRAME ; LD A,(CHANNEL) OR 0CH LD (DE),A ; SET CHANNEL BITS LD HL,IB_RS1 ; SEND RESPONSE ROUTINE LD (BTXVEC),HL ; SET TO SEND RESPONSE MSG NOTRESP: LD (OUT_COUNT),BC LD (OUT_PTR),DE ; NEXT BYTE TO SEND LD A,1 LD (OUT_STARTED),A ; YES, OUTPUT STARTED CALL CON_ON CL_0: IN A,(B_CTL) ; LOOK AT RR0 AND TBE ; ISOLATE THE TBE BIT JR Z,CL_0 ; WAIT FOR TRANSMITTER TO GET DONE LD A,FEND LD (TXCCC),A ; CHECKSUM OUT (B_DAT),A ;;;SEND FEND CHARACTER (START TXING) JP COMMUTATOR_LOOP ;KEEP LOOKING FOR NEW OPPORTUNITY SEND_ACK: CALL CON_ON LD HL,B_TAK1 LD (BTXVEC),HL ; SEND ACK ROUTINE LD A,1 LD (OUT_STARTED),A ;;;YES, OUTPUT STARTED ; ; WAIT FOR SIO ; ACL_0: IN A,(B_CTL) ;;;LOOK AT RR0 AND TBE ;;;ISOLATE THE TBE BIT JR Z,ACL_0 ;;;WAIT FOR TRANSMITTER TO GET DONE LD A,FEND OUT (B_DAT),A ;;;SEND FEND CHARACTER (START TXING) JP COMMUTATOR_LOOP ;KEEP LOOKING FOR NEW OPPORTUNITY ;***************************************************************************** ; TIMER-DRIVEN EVENTS ;***************************************************************************** RTCLOCK: ; ; COUNT DOWN MINUTES TO KICK OFF CWID ; LD HL,6000 ; MINUTE IN 1/100THS OF SEC LD (TXQT_CWID),HL ; GET TIMER VALUE INTO TIMER SLOT LD A,1 LD (TXQE_CWID),A ; ENABLE THIS EVENT LD A,(MINUTES) DEC A LD (MINUTES),A RET NZ ; INC A LD (CWIDFLAG),A ; SHOW WE NEED AN ID RET ; STARTID: ; ; SET TIMER TO GO TO NEXT CWID STATE ; LD HL,IDEVENT LD (TXQ_ADDRESSES+6),HL ; REAL TIME EVENT FOR CWID ; LD HL,6 ; 20HZ BAUD RATE FOR CWID LD (TXQT_CWID),HL ; GET TIMER VALUE INTO TIMER SLOT LD A,1 LD (TXQE_CWID),A ; ENABLE THIS EVENT ; ; RAISE RTS - NEXT TIMER EVENT WILL SEND FIRST BIT ; LD HL,CWID LD (CWIDPTR),HL XOR A LD (CWIDFLAG),A LD (LASTBIT),A ; ; ENTER SYNC MODE SO WE CAN CONTROL TONES ; DI LD A,1 OUT (A_CTL),A XOR A OUT (A_CTL),A ; INTERRUPTS OFF LD A,4 OUT (A_CTL),A LD A,00010000B ; SYNC OUT (A_CTL),A LD A,6 OUT (A_CTL),A LD A,0FFH OUT (A_CTL),A ; SYNC CHARS LD A,7 OUT (A_CTL),A LD A,0FFH OUT (A_CTL),A ; SYNC CHARS LD A,5 OUT (A_CTL),A LD A,11101011B ; RAISE RTS + TXENABLE LD (A_WR5),A OUT (A_CTL),A ; EI RET IDEVENT: ; ; 60 MS HAS PASSED - GO TO NEXT STATE ; LD HL,6 ; 20HZ BAUD RATE FOR CWID LD (TXQT_CWID),HL ; GET TIMER VALUE INTO TIMER SLOT LD A,1 LD (TXQE_CWID),A ; ENABLE THIS EVENT ; LD HL,(CWIDPTR) LD A,(HL) INC HL LD (CWIDPTR),HL CP 0FFH JP Z,ENDID LD HL,LASTBIT CP (HL) RET Z ; NO CHANGE LD (HL),A ; SAVE NEW STATE LD A,7FH OUT (A_DAT),A ; TOGGLE TONE RET ; ENDID: ; ; RESET REAL TIME EVENT TO COUNT DOWN FOR NEXT ID (29 MINS) ; PUSH BC DI IN A,(A_CTL) ;ASSURE WE ARE TALKING TO CH 0 LD C,A_CTL LD B,A_SIZE LD HL,A_INIT OTIR ;INIT SYNC (MODEM) PORT LD A,0E9H LD (A_WR5),A EI POP BC LD HL,RTCLOCK LD (TXQ_ADDRESSES+6),HL ; REAL TIME CLOCK FOR CWID ; LD HL,6000 ; MINUTE IN 1/100THS OF SEC LD (TXQT_CWID),HL ; GET TIMER VALUE INTO TIMER SLOT LD A,1 LD (TXQE_CWID),A ; ENABLE THIS EVENT LD A,29 LD (MINUTES),A RET ;----------------------------------------------------------------------------- R_DELAY: ; THIS ROUTINE EXECUTES WHEN THE TX DELAY TIMER EXPIRES. PUSH AF PUSH BC PUSH DE PUSH HL CALL STARTFRAME POP HL POP DE POP BC POP AF RET STARTFRAME: ; LD HL,TX_Q DI CALL Q_REM EI LD H,D LD L,E ; ADDR TO HL LD (TX_BUF),HL ; SAVE BUFFER ADDR FOR LATER RELEASE LD A,80H OUT (A_CTL),A ;;; RESET TX CRC ; ; HL POINTS TO BUFFER ; INC HL INC HL LD C,(HL) INC HL LD B,(HL) ; GET LENGTH INC HL LD A,(HL) ; FIRST BYTE INC HL OR A JP Z,NOTACK ; NORMAL MODE FRAME ; ; FRAME REQUIRES AN ACK - FIRST 2 BYTES ARE VALUE TO RETURN ; INC HL INC HL NOTACK: ; ; CHECK THE COUNT - DONT WANT TO TRANSMIT REAL GARBAGE ; DEC BC ; SENDING ONE HERE LD A,B CP 1 JP Z,LENOK OR A JP NZ,BADLEN ; TOO BIG ; ; LESS THAN 256 - JUST MAKE SURE ITS NOT ZERO ; LD A,C OR A JP NZ,LENOK BADLEN: LD BC,1 ; KEEP SAFE LENOK: LD (TX_COUNT),BC LD A,(HL) ; FIRST OF FRAME INC HL LD (TX_PTR),HL ; NEXT TO SEND R_DELX: DI OUT (A_DAT),A ;;; SHIP THIS CHAR TO TX MODEM LD A,1 LD (TX_STARTED),A ;;; AND, YES VIRGINA, WE'VE STARTED TX LD A,0C0H OUT (A_CTL),A ;;; RESET TX UNDERRUN/EOM LATCH EI RET ;----------------------------------------------------------------------------- R_SLOTTIME: ;WHEN SLOTTIME EVENT TIMER EXPIRES, COME HERE. RET ; WE WERE JUST WAITING, SO NOTHING ; ELSE TO DO HERE (!) ;----------------------------------------------------------------------------- R_TAIL: ;WHEN TAIL TIMER TIMES OUT, TURN OFF THE TX PUSH AF LD A,5 ;READY TO WRITE TO WR5 OF CH A DI ;;;MUST HAVE ATOMIC USE OF A_WR5 & SIO OUT (A_CTL),A ;;;NEXT CHAR TO A_CTL GOES TO WR5 LD A,(A_WR5) ;;;GRAB A_WR5 AND 0FFH-RTS ;;;TURN OFF RTS BIT THERE LD (A_WR5),A ;;;KEEP MEMORY COPY UPDATED OUT (A_CTL),A ;;;AND TURN OFF TX NOW EI POP AF RET ;;;--------------------------------------------------------------------------- IA_TBE: PUSH AF PUSH HL LD A,(TX_STARTED) CP 1 JP NZ,IA_T2 ;;; PREVIOUS FRAME FINISHED LD HL,(TX_PTR) LD A,(HL) INC HL LD (TX_PTR),HL OUT (A_DAT),A ;;; ELSE SHIP THIS CHAR OUT LD HL,(TX_COUNT) DEC HL LD (TX_COUNT),HL LD A,H OR L JR Z,IA_T1 ; FINISHED IA_T9: POP HL POP AF EI RETI ;;; JUST RETURN FROM THESE INTERRUPTS IA_T1: ; ; FINISHED WITH FRAME - WILL BE TIDIED IN BG ; LD A,0FFH LD (TX_STARTED),A POP HL POP AF EI RETI ; ; WE GET TWO END INTERRUPTS - ONE BEFORE AND ONE AFTER THE CRC ; IA_T2: ; CP 0FFH JP Z,IA_T3 ; NEED CRC ; ; END OF CRC INTERRUPT HAS OCCURRED ; ; WE MAY NEED TO GENERATE ACK - I DONT WANT TO KEEP INTS OFF TOO LONG, ; SO WILL DO NEXT FRAME PROCESSING IN BG. THE DELAY SHOULD BE VERY ; SHORT, BUT WONT CAUSE ANY HARM IF ITS A BIT LONGER ; LD A,28H OUT (A_CTL),A ;;; RESET TX INTERRUPT PENDING LD (BGACT),A LD A,(TX_Q) OR A JP NZ,IA_T21 ;;;IF MORE TO SEND, STAY KEYED UP ; START TAIL TIMER EVENT LD A,(TAILTIME) ;;; { BUG FOUND 30 SEP. IT WAS: LD H,0 ;;; "LD HL,(TAILTIME)" LD L,A ;;; [OUCH!] } LD (TXQT_TAIL),HL ;;; WAIT FOR CRC TO CLEAR TX LD A,1 ;;; 8.33 MS/CHAR AT 1200 BPS LD (TXQE_TAIL),A ;;; TAILTIME VALUE SHOULD BE >=2. IA_T21: POP HL POP AF EI RETI IA_T3: XOR A LD (TX_STARTED),A ; REALLY FINISHED LD A,28H OUT (A_CTL),A ;;; RESET TX INTERRUPT PENDING POP HL POP AF EI RETI ;;;--------------------------------------------------------------------------- ;;; GOT A CHARACTER FROM THE SIO RX INTERRUPT, DEAL WITH IT ;;; EXTENSIVE MODS 3 FEB 87 TO BE IN LINE WITH WHAT I NOW KNOW ABOUT SIO... IA_RCA: PUSH AF PUSH HL LD A,(RX_BUF+1) OR A JP NZ,IA_RC7 ;;; GO THERE IF WE ARE IN "RECEIVING" STATE ; ELSE WE ARE NOT YET RECEIVING, SO ALLOCATE BUFFER & MAKE US "RECEIVING" PUSH DE LD HL,FREE_Q PUSH BC CALL Q_REM POP BC JP Z,RESTART ; SHOULD NEVER RUN OUT IN NORMAL OPERATION LD (RX_BUF),DE ;;; TUCK AWAY ADDR OF OUR CURRENT BUFFER LD HL,4 ADD HL,DE ; TO DATA AREA LD A,(CHANNEL) LD (HL),A ; PUT KISS CONTROL BYTE IN BUFFER INC HL LD (RX_PTR),HL LD HL,1 ; CONTROL LD (RX_LEN),HL POP DE IA_RC7: LD HL,(RX_PTR) ;;; LOAD UP ADDRESS OF OUR CURRENT RX BUFFER IN A,(A_DAT) ;;; GRAB THE PENDING CHARACTER LD (HL),A INC HL LD (RX_PTR),HL LD HL,(RX_LEN) INC HL LD (RX_LEN),HL ; LENGTH LD A,H CP 1 JP NZ,ALENOK LD A,L CP BUFFLEN-262 JP NZ,ALENOK ; ; RUNNING OFF END - FLUSH FRAME ; PUSH DE LD HL,FREE_Q LD DE,(RX_BUF) ; RELEASE BUFFER CALL Q_ADDF XOR A LD (RX_BUF+1),A ;;; NOT RECEIVING POP DE POP HL LD A,33H ; ERROR RESET, WR3 OUT (A_CTL),A LD A,0D9H ; ENTER HUNT OUT (A_CTL),A ; POP AF EI RETI ALENOK: IA_RC9: POP HL POP AF EI RETI ;;; NOTHING ELSE TO DO HERE ;;;--------------------------------------------------------------------------- ;;; FROM OUT POINT OF VIEW, THIS INTERRUPT IS ONLY INTERESTING BECAUSE IT ;;; TELLS US IF WE'RE AT END OF FRAME. IA_SPECIAL: PUSH AF PUSH HL ;;; REGS WE'LL NEED LD A,1 OUT (A_CTL),A ;;; READY TO READ RR1 IN A,(A_CTL) ;;; OK, GRAB RR1 ;;; FIRST CHECK IF RX OVERRUN. THIS IS VERY BAD, SO HALT. BIT 5,A JP Z,IA_SP0 ;;; MOST OF THE TIME (ALL THE TIME?) GO THERE JP RESTART ; BPQ IA_SP0: BIT 7,A ;;; CHECK STATE OF END OF FRAME BIT JP Z,IA_SP8 ;;; ELSE SOMETHING WEIRD HAPPENED - PROBABLY ;;; RX OVERRUN. IN ANY CASE, FLUSH THIS FRAME. ;;; ERROR RESET & THEN EXIT ;;; THAT IS, TREAT LIKE IT WAS A CRC ERROR ;;; IF END OF FRAME, CHECK CRC BIT FOR VALID. IA_SP1: BIT 6,A ;;; CHECK CRC ERROR BIT JP NZ,IA_SP8 ;;; IF CRC ERROR BIT IS ON, THEN WAS CRC ERROR ;;; FIRST ENSURE THAT WE INDEED HAVE A BUFFER ALLOCATED... LD A,(RX_BUF+1) OR A JP Z,IA_SP9 ;;; IF NO BUFFER ALLOCATED, IGNORE THIS. ;;; ELSE THIS WAS A GOOD FRAME, AND WE SHOULD SHIP IT OUT TO HOST ; ; GET LENGTH AND PUT IN BUFFER HEADER, THEN PASS BUFFER TO OUT_Q ; PUSH DE LD HL,OUT_Q LD DE,(RX_BUF) INC DE INC DE LD A,(RX_LEN) LD (DE),A INC DE LD A,(RX_LEN+1) LD (DE),A DEC DE DEC DE DEC DE PUSH BC CALL Q_ADD POP BC POP DE XOR A LD (RX_BUF+1),A ;;; WE DON'T HAVE A BUFFER ALLOCATED ;;; FOR THE NEXT FRAME... JP IA_SP9 ;;; GET HERE IF THERE WAS A BAD CRC IA_SP8: LD A,(RX_BUF+1) ;;; IF WE DON'T HAVE ANY BUFFERS ;;; ALLOCATED, THEN OR A ;;;8 FEB - SET CONDITION CODES !!!!!! JP Z,IA_SP9 ;;; WE MUST NOT "RELEASE" THEM !!! 10 SEP 86 ;;; IF THEY ARE NOT ALLOCATED !!! IA_SPF: PUSH DE LD HL,FREE_Q LD DE,(RX_BUF) ; RELEASE BUFFER CALL Q_ADDF POP DE XOR A LD (RX_BUF+1),A ;;; NOT RECEIVING IF WE HAVE BAD CRC IA_SP9: LD A,30H ;;; ERROR RESET OUT (A_CTL),A IN A,(A_DAT) ;;; AVOID SPURIOUS RCA INTERRUPT ; IN A,(A_DAT) ;;; AVOID SPURIOUS RCA INTERRUPT ; IN A,(A_DAT) ;;; AVOID SPURIOUS RCA INTERRUPT ; IN A,(A_DAT) ;;; AVOID SPURIOUS RCA INTERRUPT ; ;;; AND FLUSH SILO POP HL POP AF EI RETI ;;;--------------------------------------------------------------------------- ;;; FOR EXT/STATUS INTERRUPTS ON MODEM, GET DCD STATE INTO MEMORY, AND ;;; DEALLOCATE ANY SPURIOUS BUFFERS (BUFFER STUFF DONE 30 SEP 86). IA_EXT: PUSH AF LD A,10H ;;; RESET EXT/STATUS INTERRUPTS OUT (A_CTL),A IN A,(A_CTL) ;;; GRAB RR0 AND DCD LD (DCD_STATE),A ;;;SAVE FOR TX KEYUP DCD DETECT. IS 0 IF DCD ;;;IS NOT ACTIVE, OR NON-ZERO IF IT IS ACTIVE. LD A,(RX_BUF+1) ;;; IF WE ARE NOT IN THE ;;; RECEIVING STATE... OR A ;;; THEN THERE ARE NO ALLOCATED BUFFERS AND... JP Z,IA_EX9 ;;; WE MUST NOT "RELEASE" THEM !!! 10 SEP 86 ;;; IF NO BUFFERS ALLOCATED !!! PUSH HL PUSH DE LD HL,FREE_Q LD DE,(RX_BUF) ; RELEASE BUFFER CALL Q_ADDF XOR A LD (RX_BUF+1),A ;;; NOT RECEIVING POP DE POP HL ; ; WE HAVE JUST JUNKED THE FIRST PART OF THE FRAME, BUT THE RECEIVER ; MAY WELL STILL BE RUNNING (IF DCD HAS DROPPED WITHOUT LOSS OF ; DATA, WHICH CAN HAPPEN WITH STATE MACHINE DCD). IT SEEMS A GOOD ; IDEA TO RE-ENTER HUNT MODE ; LD A,33H ; ERROR RESET, WR3 OUT (A_CTL),A LD A,0D9H ; ENTER HUNT OUT (A_CTL),A ; IA_EX9: POP AF EI RETI ;;;--------------------------------------------------------------------------- ;;; WE GET HERE WHENEVER -CTS, -DCD OR -SYNC INPUTS CHANGE, AS WELL AS BREAK ;;; DETECTION. SINCE -DCD ;;; IS ALWAYS TIED TO +5 VOLTS, WE NEED ONLY WORRY ABOUT -CTS AND -SYNC. ;;; -CTS IS WIRED TO PIN 20, DTR, OF THE RS232 CONNECTOR, AND IS SUPPOSED TO ;;; BE USED FOR HOST TO TNC HANDSHAKING; WE IGNORE THIS TRANSITION (WE ASSUME ;;; THAT THE HOST IS ALWAYS READY). WE ALSO IGNORE BREAK DETECTION. WE ARE ;;; ONLY INTERESTED IN -SYNC TRANSITIONS, SO WE CAN KEEP TIME. ;;; NOTE! THIS IS THE ONLY ROUTINE THAT IS ALLOWED TO USE THE OTHER REG SET!! ;;; DEAL WITH BREAK DETECTION... SYNC_HUNT EQU 10H IB_EXT: EX AF,AF' EXX ;;; WE WANT THE OTHER REGISTERS LD A,10H OUT (B_CTL),A ;;; RESET EXT/STATUS INTERRUPTS IN A,(B_CTL) ;;; GRAB RR0 LD D,A ;;; HOLD IT FOR A MOMENT... AND SYNC_HUNT ;;; ISOLATE THIS BIT JP Z,IB_S0 ;ELSE SYNC/HUNT IS A 1 LD A,C OR A JP Z,IB_S1 ;;; GO HERE IF STATE OF SYNC/HUNT CHANGED ;;; HERE IF SYNC/HUNT BIT DID NOT CHANGE - MAYBE SOMETHING ELSE DID.... IB_S9: LD A,D ;;; RETREIVE RRO FROM ABOVE AND BREAK_ABORT ;;; CHECK IF WE ARE DOING A BREAK/ABORT THING JP Z,IB_NBA ;;; THERE IF NO BREAK/ABORT ;;; ELSE BREAK/ABORT BIT ON, NOTE STATE CHANGE... LD A,1 LD (IN_BREAK),A ;;; SAVE IN MEM (PROBABLY CAN USE E REG...) IN A,(B_DAT) ;;; CLEAR OUT ANY NULL CHARACTER FROM BUFFER JP IB_BOK ;;; BREAK OK FOR NOW... IB_NBA: ;;;IF NO BREAK/ABORT, CHECK IF WE ARE IN BREAK/ABORT STATE. LD A,(IN_BREAK) OR A JP Z,IB_BOK ;;; NOTHING GOING ON, BREAK OK ;;; ELSE WE WERE IN BREAK MODE, AND THIS IS THE TAIL END OF A BREAK. XOR A LD (IN_BREAK),A IN A,(B_DAT) ;;; DISCARD THE SINGLE EXTRANEOUS NULL IB_BOK: IB_S99: EX AF,AF' EXX EI RETI ;;; ELSE SOMETHING ELSE & WE DON'T CARE IB_S0: ;;; SYNC/HUNT IS A 0 LD A,C OR A JP NZ,IB_S1A ;;; GO HERE IF SYNC/HUNT CHANGED JP IB_S99 ;;; ELSE NOT INTERESTED, FORGET IT ;GET HERE IF STATE OF SYNC/HUNT CHANGED IB_S1: LD C,1 JP IB_S1B IB_S1A: ;;; FIRST FIX UP C FOR NEXT TICK LD C,0 IB_S1B: ;;; HERE WHEN WE'VE SEEN A REAL "CLOCK TICK" & DEALT WITH C REG INC B LD A,B CP 12 JP NZ,IB_S99 ;;; WE ACT ON EVERY 12TH CLOCK TICK... LD B,0 ;;; SO RELOAD DIVISOR. THIS GIVE US AN ;;; EFFECTIVE INTERRUPT RATE OF 100 HZ ;;; DECREMENT ALL THE TIMERS LD HL,(TXQ_TIMERS) ;;; GET FIRST TIMER VALUE, AND ... DEC HL ;;; ... DECREMENT IT AS REQUIRED. LD (TXQ_TIMERS),HL LD HL,(TXQ_TIMERS+2) ;;; GET SECOND TIMER VALUE, AND ... DEC HL ;;; ... DECREMENT IT AS REQUIRED. LD (TXQ_TIMERS+2),HL LD HL,(TXQ_TIMERS+4) ;;; GET THIRD TIMER VALUE, AND ... DEC HL ;;; ... DECREMENT IT AS REQUIRED. LD (TXQ_TIMERS+4),HL LD HL,(TXQ_TIMERS+6) ;;; GET CWID TIMER VALUE, AND ... DEC HL ;;; ... DECREMENT IT AS REQUIRED. LD (TXQ_TIMERS+6),HL JP IB_S99 ;;;--------------------------------------------------------------------------- IB_SPECIAL: PUSH AF IB_SP9: ;;; NORMAL EXIT LD A,30H ;;; ERROR RESET OUT (B_CTL),A POP AF EI RETI ;;;--------------------------------------------------------------------------- ;;; THE TX HAS BECOME EMPTY, SHOVE A NEW CHARACTER OUT IB_TBE: PUSH AF ;;; NEW CHAR WILL RETURN IN A PUSH HL LD HL,(BTXVEC) JP (HL) ; ENTER ROUTINE ; B_TAK1: ; ; SEND CONTROL BYTE FOR POLL RESPONSE ; LD A,(CHANNEL) OR 0EH OUT (B_DAT),A LD HL,B_TAK2 ; FINISHED SENDING ACK LD (BTXVEC),HL POP HL POP AF EI RETI B_TAK2: LD HL,BTIGNORE LD (BTXVEC),HL XOR A LD (OUT_STARTED),A ;;; NOT DOING OUTPUTS ANYMORE LD A,28H ;;; NEEDED FOR ASYNC OUT (B_CTL),A ;;; RESET TX INTERRUPT PENDING POP HL POP AF EI RETI B_TNORM: ; ; NORMAL TRANSMIT ; LD HL,(OUT_COUNT) DEC HL LD (OUT_COUNT),HL LD A,H OR L JR Z,B_TDONE ; FINISHED LD HL,(OUT_PTR) LD A,(HL) INC HL LD (OUT_PTR),HL CP FESC JP Z,IB_T1A ;;; DEAL WITH FESC CHAR IN DATA STREAM CP FEND JP Z,IB_T1B ;;; DEAL WITH FEND CHAR IN DATA STREAM ;;; ELSE THIS CHAR IS NOTHING SPECIAL, SO SHOVE IT OUT OUT (B_DAT),A ;;; SHOVE IT OUT ; ; ACCUMULATE CHECKSUM ; LD H,A LD A,(TXCCC) XOR H LD (TXCCC),A POP HL POP AF EI RETI ;;; HERE IF FESC IN DATA STREAM IB_T1A: OUT (B_DAT),A ; SHIP FESC CHARACTER TO PORT LD H,A ; INCLUDE IN CHECKSUM LD A,(TXCCC) XOR H LD (TXCCC),A LD HL,B_TFESC LD (BTXVEC),HL ; SEND TFESC NEXT TIME POP HL POP AF EI RETI ;;; HERE IS FEND IN DATA STREAM IB_T1B: LD H,A ; INCLUDE IN CHECKSUM LD A,(TXCCC) XOR H LD (TXCCC),A LD A,FESC OUT (B_DAT),A LD HL,B_TFEND LD (BTXVEC),HL ; SEND TFEND NEXT TIME POP HL POP AF EI RETI B_TFESC: ; ; SEND TFESC IN NORMAL PART OF DATA (NOT CCC) ; LD A,TFESC OUT (B_DAT),A LD HL,B_TNORM LD (BTXVEC),HL ; BACK TO NRRMAL POP HL POP AF EI RETI B_TFEND: ; ; SEND TFEND ; LD A,TFEND OUT (B_DAT),A LD HL,B_TNORM LD (BTXVEC),HL ; BACK TO NRRMAL POP HL POP AF EI RETI B_TDONE: ; ; SEND CHECKSUM - BEWARE! - IT MAY BE FEND OR FESC ; LD A,(TXCCC) XOR FEND ; HAVNT SENT FEND YET ; CP FEND JP NZ,TCCC50 ; ; MUST SEND FESC, THEN TFEND ; LD A,FESC OUT (B_DAT),A ; LD HL,TCCC10 LD (BTXVEC),HL POP HL POP AF EI RETI TCCC10: LD A,TFEND OUT (B_DAT),A LD HL,BTXFEND ; NEED FEND NEXT LD (BTXVEC),HL POP HL POP AF EI RETI TCCC50: CP FESC JP NZ,IBTD00 ; NORMAL ; ; MUST SEND FESC, THEN TFESC ; OUT (B_DAT),A ; LD HL,TCCC60 LD (BTXVEC),HL POP HL POP AF EI RETI TCCC60: LD A,TFESC OUT (B_DAT),A LD HL,BTXFEND ; NEED FEND NEXT LD (BTXVEC),HL POP HL POP AF EI RETI IBTD00: ; ; CHECKSUM IS NOT SPECIAL - JUST SEND IT ; OUT (B_DAT),A LD HL,BTXFEND ; NEED FEND NEXT LD (BTXVEC),HL POP HL POP AF EI RETI BTXFEND: ; ; SEND FINAL FEND, AND SET TO RELEASE BUFFER ; LD HL,B_TFIN ; TX COMPLETE ROUTINE LD (BTXVEC),HL LD A,FEND OUT (B_DAT),A POP HL POP AF EI RETI ; HERE IF ARE COMPLETELY DONE SENDING FRAME FROM A BUFFER B_TFIN: PUSH DE ;;; NEED THIS FOR A MOMENT LD HL,FREE_Q LD DE,(OUT_BUF) CALL Q_ADDF POP DE LD HL,BTIGNORE LD (BTXVEC),HL XOR A LD (OUT_STARTED),A ;;; NOT DOING OUTPUTS ANYMORE LD A,28H ;;; NEEDED FOR ASYNC OUT (B_CTL),A ;;; RESET TX INTERRUPT PENDING POP HL POP AF EI RETI ; ; RESPONSE FRAME PROCESSING ; IB_RS1: LD HL,(OUT_COUNT) DEC HL LD (OUT_COUNT),HL LD A,H OR L JP Z,B_TFIN ; FINISHED LD HL,(OUT_PTR) LD A,(HL) INC HL LD (OUT_PTR),HL OUT (B_DAT),A ; POP HL POP AF EI RETI BTIGNORE: LD A,28H ;;; NEEDED FOR ASYNC OUT (B_CTL),A ;;; RESET TX INTERRUPT PENDING POP HL POP AF EI RETI ;;;--------------------------------------------------------------------------- ;;; GOT A CHAR FROM THE TTY PORT, DEAL WITH IT. IB_RCA: PUSH AF IN A,(B_CTL) ;;; READ RR0; FORCE REG POINTER TO BE 0 LD A,1 OUT (B_CTL),A ;;; READY TO READ RR1 IN A,(B_CTL) ;;; GRAB RR1 AND FRAMING_ERROR ;;; ISOLATE THE FE BIT JP Z,IB_RTOP ;;; NO FRAMING ERROR, SO PROCESS THIS CHAR ;;; ELSE WE HAVE A FRAMING ERROR - IGNORE THIS CHAR & FLUSH THIS FRAME... CALL STA_OFF ;;; OFF WITH THE LED! IN A,(B_DAT) ;;; FLUSH ERRONEOUS CHARACTER XOR A LD (IN_STATE),A ;;; FORCE RECEIVER TO LOOK FOR FEND LD A,(IN_BUF+1) OR A JP Z,IB_RC9 ;;; IF NO BUFFER IS ALLOCATED, DONE; EXIT. ;;; ELSE WE WERE RECEIVING A DATA SLIP FRAME, SO FLUSH IT. PUSH HL PUSH DE LD HL,FREE_Q LD DE,(IN_BUF) CALL Q_ADDF XOR A LD (IN_BUF+1),A POP DE POP HL JP IB_RC9 ;;; AND GET OUT OF HERE! IB_RTOP: LD A,(IN_STATE) ;;; GET OUR STATE MACHINE VALUE OR A JR Z,IB_R0 ;;; IN STATE 0, WAITING FOR FEND CP 1 JR Z,IB_R1 ;;; IN STATE 1, SAW FEND CP 2 JP Z,IB_R2 ;;; IN STATE 2, DATA TO FOLLOW CP 3 JP Z,IB_R3 ;;; SAW FESC, EXPECTING TFESC OR TFEND CP 100 JP Z,IDCHR1 ; ID BYTE 1 CP 101 JP Z,IDCHR2 ; ID BYTE 2 CP 10 JP Z,IB_R10 ;;; EXPECTING TXDELAY CP 20 JP Z,IB_R20 ;;; EXPECTING P VALUE CP 30 JP Z,IB_R30 ;;; EXPECTING SLOTTIME VALUE CP 40 JP Z,IB_R40 ;;; EXPECTING TAILTIME VALUE CP 50 JP Z,IB_R50 ;;; EXPECTING FULL/HALF DUPLEX VALUE ;ELSE WE DON'T KNOW WHAT HAPPENED, IGNORE IT. IB_RCJUNK: IN A,(B_DAT) XOR A LD (IN_STATE),A ;;;GO INTO IN_STATE 0, FEND HUNT IB_RC9: POP AF ;;; THROW IT AWAY, WE DON'T NEED JUNK EI RETI ;;; HERE IF WE ARE HUNTING FOR FEND CHARACTER IB_R0: CALL STA_OFF IN A,(B_DAT) CP FEND JP NZ,IB_RC9 ;;; IF WE DIDN'T SEE AN FEND, KEEP LOOKING ;;; ELSE IS AN FEND, CHANGE STATE LD A,1 LD (IN_STATE),A JP IB_RC9 ;;; GET HERE IF WE'VE SEEN FEND CHARACTER; LOOK FOR COMMAND BYTE IB_R1: CALL STA_OFF IN A,(B_DAT) CP FEND JP Z,IB_RC9 ;;; JUST ANOTHER FEND, KEEP LOOKING FOR CMD CALL STA_ON ;;;GETTING VALID SLIP; SHOW IN STA LED ; ;;; HERE IF WE DO NOT HAVE AN FEND (EXPECTING COMMAND BYTE) ; ; CHECK CHANNEL NUMBER - IF NOT OURS, FLUSH FRAME ; LD (RXCCC),A ; SAVE COMMAND BYTE - IF ITS DATA, THIS WILL ; SET UP RX CCC CORRECTLY PUSH BC LD B,A ; SAVE AND 0F0H ; GET UPPER NIBBLE LD C,A ; AND SAVE LD A,(CHANNEL) CP C ; CHANNEL FROM MSG LD A,B ; RECOVER POP BC JP NZ,NOTOURS ; WAIT FOR FEND AND 0FH ; MASK OUT CHANNEL BITS JP Z,IB_R1X ;;; 0 COMMAND MEANS DATA WILL FOLLOW CP 0EH JP Z,POLCMD ;;; 0EH IS A POLL CP 0CH ;;; 0CH IS DATA FRAME REQUIRING ACK WHEN SENT JP Z,IB_R1X CP 1 JP Z,IB_R1B ;;; 1 COMMAND MEANS TXDELAY WILL FOLLOW CP 2 JP Z,IB_R1C ;;; 2 COMMAND MEANS P(PERSISTENCE) WILL FOLLOW CP 3 JP Z,IB_R1D ;;; 3 COMMAND MEANS SLOT TIME WILL FOLLOW CP 4 JP Z,IB_R1E ;;; 4 COMMAND MEANS TAILTIME TO FOLLOW CP 5 JP Z,IB_R1F ;;; 5 COMMAND MEANS FULL/HALF DUPLEX TO COME ; ; DONT UNDERSTAND - DISCARD ; JP DUFCTL POLCMD: ; ; POLL COMMAND - SET FLAG, AND FLUSH REST OF FRAME ; LD (POLLED),A DUFCTL: NOTOURS: ;;; HERE IF WE RECEIVE BOGUS COMMAND BYTE, FLUSH REST OF FRAME CALL STA_OFF ;;;BOGOSITY, SO TURN OFF STA LED XOR A LD (IN_STATE),A ;;; GO TO STATE WHICH LOOKS FOR FEND JP IB_RC9 ;;; DATA EXPECTED, CHANGE STATE IB_R1X: ; PUSH HL PUSH DE PUSH AF ; CONTROL BYTE LD HL,FREE_Q PUSH BC CALL Q_REM POP BC JP NZ,IB_RX1 ;;;ELSE NO ROOM, FLUSH THIS FRAME POP AF POP DE POP HL ;;; KEEP STACK TIDY XOR A LD (IN_STATE),A JP IB_RC9 IB_RX1: LD (IN_BUF),DE INC DE INC DE INC DE INC DE ; SKIP POINTER AND LENGTH POP AF ; CONTROL BYTE ; ; IF CONTROL BYTE IS NONZERO, THIS MESSAGE REQUIRES AN ACK ; LD (DE),A ; SAVE CONTROL BYTE SO WE KNOW IF TO SEND ACK INC DE OR A ; CHECK STATE LD A,2 ; GET DATA STATE JP Z,SETSTATE LD A,100 ; GET ID BYTE 1 SETSTATE: LD (IN_STATE),A LD HL,-1 ; ALLOW FOR CRC LD (IN_LEN),HL ; SAVE LENGTH LD (IN_PTR),DE ; NEXT BYTE POINTER POP DE POP HL XOR A JP IB_RC9 ;;; DONE SO FAR ;;; TXDELAY TO FOLLOW, CHANGE STATE IB_R1B: LD A,10 LD (IN_STATE),A JP IB_RC9 ;;; P TO FOLLOW, CHANGE STATE IB_R1C: LD A,20 LD (IN_STATE),A JP IB_RC9 ;;; SLOTTIME TO FOLLOW, CHANGE STATE IB_R1D: LD A,30 LD (IN_STATE),A JP IB_RC9 ;;; TAILTIME TO FOLLOW, CHANGE STATE IB_R1E: LD A,40 LD (IN_STATE),A JP IB_RC9 ;;; FULL/HALF DUPLEX TO FOLLOW, CHANGE STATE IB_R1F: LD A,50 LD (IN_STATE),A JP IB_RC9 ;;; THESE BYTES ARE DATA IB_R2: IN A,(B_DAT) CP FEND JR Z,IB_R2B ;;; FEND MEANS TO QUEUE THIS BUFFER CP FESC JR Z,IB_R2A ;;; IF FESC IN DATA STREAM, SWITCH STATE IB_R3Z: PUSH HL LD HL,(IN_PTR) ; PUT CHAR IN BUFFER LD (HL),A INC HL LD (IN_PTR),HL LD H,A LD A,(RXCCC) ; MAINTAIN CHECKSUM XOR H LD (RXCCC),A LD HL,(IN_LEN) ; KEEP LENGTH INC HL LD (IN_LEN),HL LD A,H CP 1 JP NZ,BLENOK LD A,L CP BUFFLEN-262 JP NZ,BLENOK ; ; RUNNING OFF END - FLUSH FRAME ; PUSH DE JP BADCCC ; WILL RELEASE FRAME BLENOK: POP HL JP IB_RC9 ;;; DONE SO FAR ;;; FESC CHARACTER SEEN WHILE GRABBING DATA IB_R2A: LD A,3 LD (IN_STATE),A ;;; GO TO THIS OTHER STATE JP IB_RC9 ;;; FEND CHARACTER SEEN WHILE GRABBING DATA IB_R2B: ; ; END OF FRAME - CHECK CHECKSUM ; PUSH HL PUSH DE LD A,(RXCCC) OR A JP NZ,BADCCC ;; DUFF FRAME ; ; CHECKSUM IS OK - PASS FRAME ON ; LD DE,(IN_BUF) INC DE INC DE LD A,(IN_LEN) LD (DE),A INC DE LD A,(IN_LEN+1) LD (DE),A LD DE,(IN_BUF) LD HL,TX_Q PUSH BC CALL Q_ADD POP BC POP DE POP HL XOR A ;;; I WANT FENDS AT EACH END LD (IN_STATE),A ;;; GO LOOK FOR ANOTHER FRAME CALL STA_OFF ;;;DONE GETTING THIS FRAME, TURN STA LED OFF JP IB_RC9 BADCCC: ; ; BAD CHECKSUM, DISCARD FRAME ; CALL STA_OFF ;;; OFF WITH THE LED! XOR A LD (IN_STATE),A ;;; FORCE RECEIVER TO LOOK FOR FEND LD DE,(IN_BUF) LD HL,FREE_Q CALL Q_ADDF POP DE POP HL JP IB_RC9 ;;; AND GET OUT OF HERE! ;;; HERE IF WE'VE SEEN FESC IN DATA STREAM IB_R3: IN A,(B_DAT) CP TFESC JR Z,IB_R3A CP TFEND JR Z,IB_R3B ;;; ELSE WE DON'T KNOW WHAT THE HELL IT IS, SO IGNORE & KEEP COLLECTING BYTES LD A,2 LD (IN_STATE),A ;;; GO BACK INTO "DATA RECEIVING" STATE JP IB_RC9 ;;; HERE IF WE'VE SEEN TFESC AFTER AN FESC IN DATA STREAM; WRITE AN FESC IB_R3A: LD A,2 LD (IN_STATE),A ;;; GET OUT OF ESCAPED MODE LD A,FESC JP IB_R3Z ;;; HERE IF WE'VE SEEN TFEND AFTER FESC IN DATA STREAM; WRITE FEND IB_R3B: LD A,2 LD (IN_STATE),A ;;; GET OUT OF ESCAPED MODE LD A,FEND JP IB_R3Z ;;; REST IS SAME AS FOR TFESC CASE ;;; THIS CHARACTER IS INTERPRETED AS TXDELAY IB_R10: IN A,(B_DAT) LD (TXDELAY),A XOR A LD (IN_STATE),A ;;; GO BACK TO FEND HUNT STATE JP IB_RC9 ;;; THIS CHARCTER IS P, PERSISTENCE VALUE IB_R20: IN A,(B_DAT) LD (PERSISTENCE),A XOR A LD (IN_STATE),A ;;; GO BACK TO FEND HUNT STATE JP IB_RC9 ;;; THIS CHARACTER IS SLOTTIME VALUE IB_R30: IN A,(B_DAT) LD (SLOTTIME),A XOR A LD (IN_STATE),A ;;; GO BACK TO FEND HUNT STATE JP IB_RC9 ;;; THIS CHARACTER IS TAILTIME VALUE IB_R40: IN A,(B_DAT) LD (TAILTIME),A XOR A LD (IN_STATE),A ;;; GO BACK TO FEND HUNT STATE JP IB_RC9 ;;; THIS CHARACTER IS FULL/HALF DUPLEX VALUE ;;; 0 MEANS HALF DUPLEX, NON-ZERO MEANS FULL DUPLEX IB_R50: IN A,(B_DAT) LD (FULL_DUPLEX),A XOR A LD (IN_STATE),A ;;; GO BACK TO FEND HUNT STATE JP IB_RC9 IDCHR1: ; ; GET FIRST ID BYTE ; IN A,(B_DAT) PUSH HL LD HL,(IN_PTR) ; PUT CHAR IN BUFFER LD (HL),A INC HL LD (IN_PTR),HL LD A,101 LD (IN_STATE),A ;;; GO TO 'GET SECOND ID' POP HL JP IB_RC9 ;;; DONE SO FAR IDCHR2: ; ; GET SECOND ID BYTE ; IN A,(B_DAT) PUSH HL LD HL,(IN_PTR) ; PUT CHAR IN BUFFER LD (HL),A INC HL LD (IN_PTR),HL LD A,2 LD (IN_STATE),A ;;; GO BACK INTO "DATA RECEIVING" STATE POP HL JP IB_RC9 ;;; DONE SO FAR ;----------------------------------------------------------------------------- STA_ON: ;TURN THE STA LED ON. ASSUMES THAT INTERRUPTS ARE DISABLED! PUSH AF LD A,5 OUT (A_CTL),A ;;; READY TO WRITE WR5 LD A,(A_WR5) ;;; GET MEMORY COPY AND 0FFH-ALED ;;; SET DTR BIT TO 0 SO LED GOES ON OUT (A_CTL),A ;;; ACTUALLY TURN ON STA LED NOW... LD (A_WR5),A ;;; UPDATE MEMORY COPY POP AF RET ;----------------------------------------------------------------------------- STA_OFF: ;TURN THE STA LED OFF. ASSUMES THAT INTERRUPTS ARE DISABLED! PUSH AF LD A,5 OUT (A_CTL),A ;;; READY TO WRITE WR5 LD A,(A_WR5) ;;; GET MEMORY COPY OR ALED ;;; SET DTR BIT TO 1 SO LED GOES OFF OUT (A_CTL),A ;;; ACTUALLY TURN OFF STA LED NOW... LD (A_WR5),A ;;; UPDATE MEMORY COPY POP AF RET ;THESE ROUTINES MUST BE CALLED WITH INTERRUPTS DISABLED! ;----------------------------------------------------------------------------- STA_FLIP: PUSH AF PUSH BC IN A,(A_CTL) ;;;ASSURE WE ARE TALKING TO CH 0 LD A,5 OUT (A_CTL),A ;;; READY TO WRITE WR5 LD A,(A_WR5) ;;; GET MEMORY COPY LD B,A ;;; SAVE ORIGINAL FOR A MOMENT... AND ALED ;;; CHECK THE STA LED BIT LD A,B ;;; RETREIVE ORIGINAL JP Z,STA_F0 ;;; BIT IS A 0, SO LED IS ON, MAKE OFF ; ELSE MAKE IT GO ON (BECAUSE IT IS NOW OFF) AND 0FFH-ALED ;;; SET DTR BIT TO 0 SO LED GOES ON JP STA_F1 STA_F0: OR ALED ;;; SET DTR BIT TO 1 SO LED GOES OFF STA_F1: OUT (A_CTL),A ;;; ACTUALLY TURN OFF STA LED NOW... LD (A_WR5),A ;;; UPDATE MEMORY COPY POP BC POP AF RET ;----------------------------------------------------------------------------- CON_ON: PUSH AF LD A,5 DI OUT (B_CTL),A LD A,BLEDON LD (B_WR5),A ;;; SAVE IN MEM FOR FLIP ROUTINE OUT (B_CTL),A EI POP AF RET ;----------------------------------------------------------------------------- CON_OFF: LD A,5 DI ; PROTECT SIO OUT (B_CTL),A LD A,BLEDOFF OUT (B_CTL),A LD (B_WR5),A ;;; SAVE IN MEM FOR FLIP ROUTINE EI RET ;----------------------------------------------------------------------------- CON_FLIP: PUSH AF PUSH BC IN A,(B_CTL) ;;;ASSURE WE ARE TALKING TO CH 0 LD A,5 OUT (B_CTL),A ;;; READY TO WRITE WR5 LD A,(B_WR5) ;;; GET MEMORY COPY LD B,A ;;; SAVE ORIGINAL FOR A MOMENT... AND BLED ;;; CHECK THE CON LED BIT LD A,B ;;; RETREIVE ORIGINAL JP Z,CON_F0 ;;; BIT IS A 0, SO LED IS ON, MAKE OFF ;ELSE MAKE IT GO ON (BECAUSE IT IS NOW OFF) AND 0FFH-BLED ;;; SET DTR BIT TO 0 SO LED GOES ON JP CON_F1 CON_F0: OR BLED ;;; SET DTR BIT TO 1 SO LED GOES OFF CON_F1: OUT (B_CTL),A ;;; ACTUALLY TURN OFF CON LED NOW... LD (B_WR5),A ;;; UPDATE MEMORY COPY POP BC POP AF RET ; ; ; QUEUE HANDLING ROUTINES ; ; EACH QUEUE CONSISTS OF A TWO BYTE HEADER AND A NUMBER OF ENTRIES, ; THE FIRST TWO BYTES OF EACH IS A FORWARD CHAIN POINTER ; THE POINTER IS THE ADDRESS OF THE NEXT ENTRY, STORED WITH THE ; HIGH ORDER BYTE FIRST (IE NOT THE NORMAL Z80 WAY ROUND) ; A ZERO FIRST BYTE INDICATES END OF CHAIN ; ; THE FOLLOWING ROUTINES ARE PROVIDED:- ; ; ADD ENTRY TO QUEUE - ON RETURN A CONTAINS NUMBER OF ENTRIES ; WHICH WERE ON THE QUEUE PRIOR TO THE CALL ; ; GET FIRST ENTRY - Z FLAG SET IF QUEUE EMPTY ; ; REMOVE FIRST ENTRY - Z FLAG SET IF QUEUE EMPTY ; ; ; ALL ROUTINES REQUIRE HL SET TO QUEUE HEADER ON ENTRY ; DE CONTAINS THE ADDRESS OF THE BLOCK FOUND OR TO BE ADDED ; ; Q_ADDF: ; ADD TO FRONT OF Q LD A,(HL) ; OLD Q HEADER LD (DE),A ; CHAIN TO NEW ONE INC HL INC DE LD A,(HL) ; OLD Q HEADER LD (DE),A ; CHAIN TO NEW ONE DEC DE ; RESET BUFFER ADDR LD (HL),E ; CHAIN NEW BUFFER TO Q HEADER DEC HL LD (HL),D LD HL,QCOUNT INC (HL) RET ; Q_ADD: XOR A ; NUMBER IN QUEUE LD (DE),A ; CLEAR CHAIN POINTER IN NEW BUFFER Q_ADD1: LD B,(HL) ; FIRST BYTE OF POINTER INC B DEC B ; 0BEST WAY TO SET FLAGS JR Z,Q_ADD5 ; END ; INC HL LD L,(HL) LD H,B ; HL = ADDR OF NEXT BUFFER INC A ; NO OF ENTRIES JP Q_ADD1 ; LOOP TILL END FOUND ; Q_ADD5: LD (HL),D ; CHAIN NEW BLOCK INC HL LD (HL),E RET ; ; ; Q_FIRST EQU $ LD D,(HL) INC D DEC D ; TO SET FLAGS RET Z INC HL LD E,(HL) RET ; ; ; Q_REM: CALL Q_FIRST ; SET FIRST ENTRY RET Z ; QUEUE EMPTY ; DEC HL EX DE,HL ; DE = Q HEADER HL = FIRST ENTRY LD B,(HL) INC HL LD C,(HL) ; BC = NEW FIRST ENTRY ; DEC HL EX DE,HL ; HL = HEADER LD (HL),B INC HL LD (HL),C ; HEADER NOW UPDATED ; RET ; DIAG: PUSH AF DIAG00: IN A,(B_CTL) ; LOOK AT RR0 AND TBE ; ISOLATE THE TBE BIT JR Z,DIAG00 ; WAIT FOR TRANSMITTER TO GET DONE POP AF OUT (B_DAT),A ;;;SEND FEND CHARACTER (START TXING) RET RESTART: DI JP 0 ; REBOOT COND PROM DEFS START+8000H-$ ENDC FREE_RAM DEFS 0 ;----------------------------------------------------------------------------- ; THESE ARE THE TX REAL-TIME ROUTINE DATA STRUCTURES. THEY ARE USED FOR ; TIMING REQUIRED WITH TX CONTROL. THERE ARE 3 ACTIONS THAT MUST BE TIMED: ; 1) TXR_DELAY TX DELAY TIMER (FOR TXDELAY FUNCTION) ; 2) TXR_SLOTTIME PART OF P-PERSISTENCE BACKOFF ; 3) TXR_TAIL TIMER TO BE SENDING SYNCS BEFORE DROPPING RTS ; THE DATA STRUCTURE CAN BE THOUGHT OF LOGICALLY AS THIS: ; ; +------------------------+ ; | ROUTINE ENABLED (BYTE) | IS 0 IF NOT ENABLED, NON ZERO IF ENABLED ; +------------------------+--------------------------------------+ ; | POINTER TO ROUTINE TO EXECUTE WHEN TIMER EXPIRES (WORD) | ; +---------------------------------------------------------------+ ; | 16-BIT DOWNCOUNTER TIMER VALUE, IN 10S OF MILLISECONDS (WORD) | ; +---------------------------------------------------------------+ ; ; THE DATA STRUCTURE HAS ONE ENTRY FOR EACH OF THE 3 TIMER EVENTS. PHYSICALLY ; IT IS ORGANIZED AS 3 SEPARATE LISTS, ONE FOR EACH OF THE ENABLES, ONE FOR ; EACH OF THE ROUTINE POINTERS, AND ONE FOR EACH OF THE TIMER VALUES. ; ; AN INTERUPT ROUTINE, RUNNING AT 10 MILLISECOND TICKS, DECREMENTS THE VALUES ; IN EACH OF THE DOWNCOUNT TIMER WHETHER A ROUTINE IS ENABLED OR NOT. WHEN ; DOWNCOUNT VALUE GOES TO 0 (OR NEGATIVE) THEN THE ROUTINE "FIRES". THIS ; CHECKING FOR "FIRING" HAPPENS AT NON-INTERRUPT LEVEL IN THE COMMUTATOR LOOP. ; WITH THIS SCHEME, THE MINIMUM TIME BEFORE FIRING IS 10 MILLISECONDS, AND THE ; MAXIMUM TIME IS 327.67 SECONDS (OVER 5 MINUTES). FOR EXAMPLE, FOR A ; TXDELAY OF 600 MILLISECONDS, THE TIMER WOULD GET LOADED WITH DECIMAL 60. ; ; WHEN A ROUTINE FIRES, IT GETS MARKED AS "DISABLED", SO YOU'D NEED TO ; EXPLICITLY RE-ENABLE IT IF THIS IS REQUIRED ; NOTE TOO THAT A CLOCK COULD BE EASILY IMPLEMENTED. IF WE INSERTED ANOTHER ; EVENT INTO OUR LIST WITH A TIMEOUT OF 100, THEN EVERY SECOND A ROUTINE WOULD ; BE CALLED. IN THAT ROUTINE, WE COULD INCREMENT THE SECONDS FIELD (AND ; POSSIBLY MINUTES, HOURS, DAYS, YEARS FIELDS) OF A TIME-OF-DAY CLOCK. WE ; WOULD IMMEDIATELY RE-ACTIVATE THIS TIMER TO GET THE NEXT TICK, ETC. TXQ_ENABLES DEFS 4 ; 4 BYTES FOR THE ENABLES TXQ_ADDRESSES DEFS 8 ; 4 WORDS FOR THE ROUTINE POINTERS TXQ_TIMERS DEFS 8 ; 4 WORDS FOR THE ROUTINE TIMERS ; NOTE THE LAST SLOT IN THIS TABLE IS FOR R_TEST ROUTINE, WHICH BLINKS STA LED ; IT IS NOT USED NORMALLY, JUST FOR HELPING ME DEBUG THIS! ; SOME EQUATES TO SAVE US FROM DOING CONTORTED THINGS WHEN WE WANT TO CHECK IF ; A ROUTINE IS ENABLED IN PLACES OTHER THAN THE COMMUTATOR LOOP, OR FOR ; ENABLING ROUTINES, ETC. TXQE_DELAY EQU TXQ_ENABLES+0 TXQE_SLOTTIME EQU TXQ_ENABLES+1 TXQE_TAIL EQU TXQ_ENABLES+2 TXQE_CWID EQU TXQ_ENABLES+3 ; SAME IDEA, BUT FOR THE TIMER VALUES TXQT_DELAY EQU TXQ_TIMERS+0 TXQT_SLOTTIME EQU TXQ_TIMERS+2 TXQT_TAIL EQU TXQ_TIMERS+4 TXQT_CWID EQU TXQ_TIMERS+6 ; WE DON'T DO THIS FOR THE ROUTINE ADDRESSES, SINCE THEY DON'T CHANGE ONCE ; THEY ARE INITIALIZED. TXDELAY DEFS 1 ; TRANSMITTER DELAY TIME VALUE PERSISTENCE DEFS 1 ; PERSISTENCE VALUE SLOTTIME DEFS 1 ; SLOT TIME VALUE TAILTIME DEFS 1 ; TX TAIL TIME VALUE FREE_Q DEFS 2 ; ADDRESS OF 1ST BUFFER ON FREE LIST ; ; RX BUFF IS FOR MESSAGE FROM RADIO PORT ; RX_BUF DEFS 2 ; ADDRESS OF CURRENT RECEIVE BUFFER RX_PTR DEFS 2 ; ADDRESS IN RX BUFFER RX_LENGTH DEFS 2 ; FRAME LENGTH RX_FLUSHING DEFS 1 ; IS NON-0 IF WE RAN OUT OF BUFFER ; SPACE AND ARE CURRENTLY FLUSHING THIS ; FRAME BEING RECEIVED. USED BY ; IA_RCA AND RESET BY IA_EXT. ; ; IN_BUF IS FOR MESSAGE FROM PC ; IN_BUF DEFS 2 ; ADDR OF CURRENT INPUT BUFFER IN_PTR DEFS 2 ; ADDR OF 1ST INPUT BUFFER IN_LEN DEFS 2 ; LENGTH OF INPUT MSG IN_STATE DEFS 1 ; 1 ; INPUT STATE MACHINE STATE ; ASSUME THAT WE'VE SEEN AN FEND FROM ; (NON-EXISTENT) "PREVIOUS" FRAME. THIS ; MEANS THAT WHEN WE ARE RECEIVING DATA ; FROM USER, THERE NEED BE ONLY THE ; FEND CHAR AT THE END OF A FRAME, AND ; NOT AT THE BEGINNING (ALTHOUGH IF A ; FEND IS AT THE BEGINNING, IT IS ; IGNORED.) OUT_STARTED DEFS 1 ; OUTPUT NOT STARTED YET (LOGICAL VAR) ; ; OUT IS FOR MESSAGES TO PC ; OUT_Q DEFS 2 ; ADDRESS OF BUFFER TO BE OUTPUT RS232 OUT_PTR DEFS 2 ; POSITION IN OUTPUT BUFFER OUT_BUF DEFS 2 ; ADDR OF BUFFER WE ARE NOW OUTPUTTING OUT_COUNT DEFS 2 ; BYTES LEFT TO SEND ; ; TX IS FOR MESSAGES TO RADIO PORT ; TX_STARTED DEFS 1 ; NON-ZERO IF WE'VE BEGUN TXING CHARS ; TX_Q DEFS 2 ; BUFFERS TO SEND (RADIO PORT) TX_PTR DEFS 2 ; POSITION IN CURRENT BUFFER TX_BUF DEFS 2 ; BUFFER BEING TX'ED TX_COUNT DEFS 2 ; BYTES LEFT TO SEND TX_OUTSTANDING DEFS 1 ; NUMBER OF TX CBUFS QUEUED UP FOR TX DCD_STATE DEFS 1 ; IS NON 0 IF DCD LED IS ON IN_BREAK DEFS 1 ; NON-ZERO IF WE ARE IN A BREAK DETECT ; ON THE ASYNC PORT FULL_DUPLEX DEFS 1 ; NOT INITIALLY FULL DUPLEX A_WR5 DEFS 1 ; ALEDOFF ; STATE OF STA LED & RTS (PTT) LINE, ; MAINLY... (FOR CH A ONLY [MODEM] ) B_WR5 DEFS 1 ; BLEDOFF DEFS 1 ; WHY?? MINUTES DEFS 1 ; FOR CWID TIMING CWIDFLAG DEFS 1 ; ID OUTSTANDING FLAG CWIDPTR DEFS 2 ; NEXT CWID BIT TO SEND LASTBIT DEFS 1 ; CURRENT CWID STATE POLLED DEFS 1 ; POLL RECEIVED FLAG TXCCC DEFS 1 ; ASYNC TX CHECKSUM RXCCC DEFS 1 ; ASYNC RX CHECKSUM QCOUNT DEFS 1 ; LENGTH OF FREE Q BGACT DEFS 1 ; SET WHEN BUFFER HAS BEEN SENT AND ; REQUIRES B/G PROCESSING TO AND ACK ; (IF NEEDED) AND SEND NEXT FRAME ; BTXVEC DEFS 2 ; ROUTINE TO PROCESS NEXT BCHAN TX INT DEFS 10 BUFFERS DEFS 0 ; END OF ALL CODE & PREDEFINED DATA ; ; BUFFER POOL FOLLOWS THIS - FORMAT IS: ; ; CHAIN WORD 2 BYTES ; LENGTH 2 BYTES ; DATA 340 BYTES - ENOUGH FOR 10 CALLS, CTRL PID 256 DATA ; AND A BIT FOR LUCK ; BUFFLEN EQU 344 ; ; LET ASSEMBLER CALCULATE SIZE OF BUFFER POOL ; BUFF8K EQU (09F00H-BUFFERS)/BUFFLEN ; ALLOWING 256 FOR STACK BUFF16K EQU (0BF00H-BUFFERS)/BUFFLEN BUFF32K EQU (0FF00H-BUFFERS)/BUFFLEN ; NOTES ON NOMENCLATURE: ; OUT = TO TTY PORT; IN = FROM TTY PORT ; TX = TO MODEM; RX = FROM MODEM ; ; ;;; MEANS THAT THAT CODE EXECUTES WITHOUT INTERRUPTS ENABLED (EXCEPT ; FOR THE INITIALIZATION CODE) ; ; ; I HAVE BEEN CAREFUL WITH JR/JP USE. I USE JP WHEN THE JUMP IS LIKELY AND ; WHERE SPEED IS IMPORTANT. I USE JR WHEN THE JUMP IS UNLIKELY SO THAT I CAN ; SAVE A FEW CYCLES. JP ALWAYS USES 10 CYCLES WHETHER IT JUMPS OR NOT, BUT ; JR USES EITHER 7 OR 12 T STATES, NO JUMP/JUMP, RESPECTIVELY. ; BUFFERS KEPT HERE AT END. END START