;FILE: Pass.asm -- Firmware for Cortron Kbd 8051 of N-GEN keyboard. ; ; ; HysPtr SET R0 ;Points to the location of the hysteresis bit Bufin SET R1 ;Used to load output buffers Inbyt SET R2 ;Input Byte Register BitMsk SET R3 ;Mask to select bits from hysteresis bitmap CurAdd SET R4 ;Contains location of next key to be scanned DlyCnt SET R5 ;Register used for start-up IDSYNC byte count TempIn SET R6 ;Temporary Input Byte (for decoding) TempR1 SET R7 ;Temporary storage for buffer loader (R1) ; ; ; ; REGISTER BANK 2 (HARDWARE UART) ; ;RO USED TO PLACE DATA INTO SBUF FOR TRANSMISSION ;R1 TEMP HOLDING OF INPUT BYTE ;R2-R7 USED FOR PART OF THE STACK ; ; ; ; The I/O Pin assignment is as follows: ; ;P00-P03: Key switch matrix column address ALL KEY SWITCHES CAN BE ADDRESSED ;P04-P07: Key switch matrix row address IN TURN BY INCREMENTING AN ; ADDRESS REGISTER FROM 00H TO 7FH, ; AND WRITING THE CONTENTS OF THAT ; REGISTER TO I/O PORT P0. ; ; ; ;P10: Switch Select Enable (SSE) ;P11: Hysteresis signal (0 = hysteresis on, 1 = hysteresis off) ;P12: 01 signal (Phase 1, which enables input of key switch state) ;P13: OUTPUT DATA A ;P14-P17: Not used (jumpers for future use) ; ; ;P20-P27: control key station LED's ; ; ;P30 (RXD): Input data B (UART) ;P31 (TXD): Output data B (UART) ;P32,P34 (INT0,T0): Input data A ;P35 (T1): Input of key switch state (1 = key up, 0 = key down) ;P33,36,37: Not used ; data_seg SEGMENT DATA bit_seg SEGMENT BIT bit_data SEGMENT DATA BITADDRESSABLE ; ; Declare constants: Delay EQU 11H ;With this value, invokation of Static routine Empty EQU 80H Rset EQU 92H CheckROM EQU 8CH Echo EQU 9EH LED03 EQU 0A0H LED47 EQU 0B0H HAND EQU 0C0H ;LEFT- OR RIGHT-HAND QUERY PASTHR EQU 0D0H ;ALLOW INBOUND PASS-THRU NOPASS EQU 0D1H ;PREVENT INBOUND PASS-THRU RINPAS EQU 0DFH ;RESET PASS-THRU SINPAS EQU 0CEH ;SET PASS-THRU IDSYNC EQU 0FEH ;ID-SYNC BYTE ; K1IDA EQU 0B0H ;K1 KBD ID CODE A (BSD1 0000) ; B = BOOTABLE = 1 ; S = 0 = SPACE BAR UP ; D = DUAL-PORTED DEVICE = 1 ; 1 = reserved bit TBD ; K1IDA1 EQU 0F0H ;K1 KBD ID CODE A1 (BSD1 0000) ; B = BOOTABLE = 1 ; S = 1 = SPACE BAR DOWN ; D = DUAL-PORTED DEVICE = 1 ; 1 = reserved bit TBD ; K1IDB EQU 04H ;K1 KBD ID CODE B (TRANSMITTED SECOND) RPASOT EQU 0DFH ;RESET OUTBOUND PASS-THRU SPASOT EQU 0CEH ;SET OUTBOUND PASS-THRU NOPTDV EQU 0AH ;'NO PT DEV PRESENT' CODE SPCADD EQU 7EH ;KEY SWITCH MATRIX ADDRESS OF SPACE BAR ; ; ; Assign memory DSEG AT 012H ; ; ;SET UP STACK ; STACK: DS 10H ;16 BYTES FOR STACK ; RSEG bit_data fLED: DS 1 ; RSEG bit_seg ; LOCK: DBIT 1 ;LOCK FOR SHARED MEMORY 1 = available for use F1: DBIT 1 ;0 = difference was found WS?: DBIT 1 ;0 = WORKSTATION AT PORT A, 1 = PORT B fEcho: DBIT 1 ;1 = true, 0 = false KSDOWN: DBIT 1 ;1 = KEYS DOWN DURING LAST SCAN NOSERO: DBIT 1 ;1 = SERIAL OUTPUT INACTIVE DOTIME: DBIT 1 ;1 = TIMER INTERRUPT PENDING PSTHRU: DBIT 1 ;0 = DISALLOW INBOUND PASS-THRU, ;1 = ALLOW INBOUND PASS-THRU. ; THIS PASS-THRU REFERS TO THE SECONDARY ; (I.E. "MOUSE") POINTING DEVICE-TO-WORK ; STATION DATA PATH. PASOUT: DBIT 1 ;0=DISALLOW OUTBOUND PASS-THRU (WS TO PT DEV), ; TRANSMISSION IS MEANT FOR KBD. ;1=FORCE OUTBOUND PASS-THRU (TRANS. MEANT FOR PT DEV). ; ; SETOK: DBIT 1 ;1 = PORTS HAVE BEEN IDENTIFIED IDSDAT: DBIT 1 ;TIMER NEED TO CALL PUTA W/ CONTINUITY PROTECT.? TDCLAB: DBIT 1 ;TIMER NEED TO CALL PUTB W/ CONTINUITY PROTECT.? TDOCLB: DBIT 1 ;TIMER NEED TO CALL PUTB ? SDCLBA: DBIT 1 ;SERIO NEED TO CALL PUTA W/ CONTINUITY PROTECT.? SDOCLA: DBIT 1 ;SERIO NEED TO CALL PUTA? IDSDBT: DBIT 1 ;SERIO NEED TO CALL PUTB W/ CONTINUITY PROTECT.? ; CODE2: DBIT 1 SCODE2: DBIT 1 GTSYNC: DBIT 1 GTSYNS: DBIT 1 ONSYNC: DBIT 1 ONSYNS: DBIT 1 ONERPS: DBIT 1 ; DOTWO: DBIT 1 ;1=NEED TO DO TWO CONTIGUOUS KBD TRANSMISSIONS. ; ; ; ; INPASS: DBIT 1 ;1=INBOUND PASS-THRU MODE SET (AM SENDING ;PT DEV -TO- WS DATA). ;0=INBOUND PASS-THRU MODE RESET (AM NOT ;SENDING PT DEV -TO- WS DATA). ; DINAA: DBIT 1 ;1=NEED TO RETURN TO TRANS. IDCODE FROM ; PORT A TO PORT A. DINBA: DBIT 1 ;1=NEED TO RETURN TO TRANS. PT. DEV. DATA ; FROM PORT B TO PORT A DINBB: DBIT 1 ;1=NEED TO RETURN TO TRANS. IDCODE FROM ; PORT B TO PORT B. DINAB: DBIT 1 ;1=NEED TO RETURN TO TRANS. PT DEV DATA ; FROM PORT A TO PORT B. ; FULBUF: DBIT 1 ;1 INDICATES A FULL OUTPUT BUFFER. ; POSSIBLE LOSS OF POINTING DEVICE DATA. LOST: DBIT 1 ;1 INDICATES LOSS OF MOUSE DATA. DETERMINES ; FORMAT OF 'SET-INBOUND-PASS-THRU' CODE. ; RSEG data_seg ; LEDAPT: DS 1 ;TEMP HOLDING OUTPUT A PUTTER DARGPT: DS 1 ;DATA A OUTPUT PUTTER DARGTK: DS 1 ;DATA A OUTPUT TAKER LEDBPT: DS 1 ;TEMP HOLDING OUTPUT B PUTTER DBRGPT: DS 1 ;DATA B OUTPUT PUTTER DBRGTK: DS 1 ;DATA B OUTPUT TAKER ; IDCOD1: DS 1 ;POINTING DEVICE CODE (DEFAULT=00H) IDCOD2: DS 1 ;POINTING DEVICE CODE TSCR1: DS 1 ;TIMER SCRATCH LOCATION TSCR2: DS 1 ;TIMER SCRATCH LOCATION SESCR1: DS 1 ;SERIO SCRATCH LOCATION SESCR2: DS 1 ;SERIO SCRATCH LOCATION ; COUNT: DS 1 ;COUNT OF IDSYNC BYTES K1ID: DS 1 ;K1 IDCODE1 ; LSTKBD: DS 1 ;LOC OF LAST KBD DATA ; ; RGOUTA: DS 24 LIMRGOUTA: ; RGOUTB: DS 24 LIMRGOUTB: ; rgFHys: DS 20 limFHys: ; ; ; ; Start of code: CSEG ORG 0 JMP Init ;Here on Reset ORG 3 JMP EXTINT ;INPUT PORT A DATA ORG 0BH ;Here on Timer/Counter Interrupt ; TCINT: JB LOCK,OKTIMR ;OK TO ENTER CRITICAL AREA? SETB DOTIME ;NO: SET TIMER-INTERRUPT-PENDING FLAG RET ;DO NOT EXECUTE RETI INSTRUCTION. DO NOT WANT ;INTERRUPTS REENABLED YET. OKTIMR: JMP TINTR ; ORG 023H JMP SERIO ;SERIAL I/O PORT Init: MOV HysPtr,#7FH ;Prepare to clear all RAM ClLoop: MOV @HysPtr,A ;clear RAM DJNZ HysPtr,ClLoop ;Loop until done CLR P1.0 ;TURN OFF SWITCH SELECT ENABLE (SSE) SETB P1.1 ;TURN OFF HYSTERESIS (HYS) CLR P1.2 ;TYRN OFF PHASE 1 (01) SETB P1.3 ;SET DATA OUT A TO MARK SETB TXD ;SET DATA OUT B TO MARK MOV DARGPT,#RGOUTA ;set up output MOV DARGTK,#RGOUTA ; putters MOV DBRGPT,#RGOUTB ; and MOV DBRGTK,#RGOUTB ; takers MOV LEDAPT,#RGOUTA MOV LEDBPT,#RGOUTB MOV SP,#STACK-1 ;INIT STACK POINTER MOV IDCOD2,#NOPTDV ;DEFAULT PT DEV IDCODE IS 'NO DEVICE' MOV LSTKBD,#EMPTY ;SET LAST KBD CHAR BUFFER = 'NO CHAR' SETB NOSERO ;SET SERIAL OUTPUT TO INACTIVE CLR A ; ; ; SET UP SOFTWARE UART AT PORT A ; MOV R5,#1 ;LOAD OUTPUT A TINTR COUNTER MOV R6,A ;CLEAR OUTPUT A SHIFT COUNTER DEC A MOV R3,A ;DISABLE INPUT A ROUTINE UNTIL EXT INT RECEIVED MOV TH1,#0F4H ;RELOAD VALUE FOR UART BAUD RATE (1200 BAUD) MOV TL1,#0F4H ;START VALUE FOR UART BAUD RATE MOV TH0,#0B0H ;RELOAD VALUE FOR SOFTWARE UART BAUD RATE (5 X UART BAUD RATE) MOV TL0,#0B0H ;START VALUE FOR SOFTWARE BAUD RATE MOV IE,#093H ;ALLOW SERIO,TIMER0 & XINT0 INTERRUPTS (ALL SAME PRIORITY) ;TIMER1 INTERRUPT NOT ALLOWED. ROLLOVER WILL SET BAUD RATE BUT NO INTERRUPT. MOV TMOD,#022H ;TIMERS 0 AND 1 IN MODE 1 (AUTO RELOAD). TIMER1 CONTROLS UART BAUD RATE MOV SCON,#070H ;SERIO IN MODE 1, RECEIVE ENABLED MOV TCON,#050H ;START TIMERS 0 & 1; SET EXTINT0 TO EDGE TRIGGERED (1 - 0 TRANSITION) ;INTEL DOCUMENTATION IS WRONG. SETTING BIT IT0 ;OF BYTE TCON TO 1 (ONE) DOES NOT MAKE THE EXTERNAL ;INTERRUPT 0 EDGE TRIGGERED AS DOCUMENTED. SETTING ;IT0 TO 0 (ZERO) DOES MAKE THE EXTERNAL INTERRUPT ;0 EDGE TRIGGERED. ; SETB LOCK ;OPEN SHARED MEMORY FOR USE ; ; ; LOOK FOR SPACE BAR DEPRESSION. IF FOUND, USE K1IDA1 ; FOR KBD IDCODE WITH S BIT SET MEANING 'SPACE BAR DOWN'. ; ; MOV K1ID,#K1IDA MOV A,#SPCADD ;ADDRESS OF SPACE BAR MOV P0,A ;OUTPUT ADDRESS TO P0 SETB P1.2 ;OUTPUT PHASE 1 SETB P1.0 ;AND SSE NOP JB T1,NOSPC ;JUMP IF NO SPACE BAR CLR P1.2 ;Clear phase 1 CLR P1.0 ;AND SSE CALL Static ;Wait for possible noise spike to settle SETB P1.2 ;Output phase 1 again SETB P1.0 ;AND SSE NOP ;Must delay before reading key JB T1,NOSPC ;Test key again, jump if noise spike MOV K1ID,#K1IDA1 NOSPC: CLR P1.2 ;CLEAR PHASE 1 CLR P1.0 ;AND SSE ; ; USING 1 ;SELECT BACKROUND LEVEL REGISTER BANK CLR PSW.4 SETB PSW.3 ; ; SEND IDSYNC STREAM AND ID CODES TO BOTH PORTS. ; MOV DLYCNT,#120 SENDID: MOV A,#IDSYNC CALL PUTA MOV A,#IDSYNC CALL PUTB DJNZ DLYCNT,SENDID MOV A,K1ID ;TRANSMIT GENERIC BYTE FIRST CALL PUTA MOV A,K1ID CALL PUTB MOV A,#K1IDB ;TRANSMIT UNIQUE ID BYTE SECOND CALL PUTA MOV A,#K1IDB CALL PUTB MOV DARGPT,LEDAPT ;UPDATE OUTPUT ROUTINES MOV DBRGPT,LEDBPT ; ACKWAT: JNB SETOK,ACKWAT ;WAIT HERE 'TIL OS REPORTS IN CLR fEcho ;RESET fECHO MOV fLED,#00H ;TURN ALL THE LED'S OFF IN BITMAP MOV P2,fLED ;AND AT PORT 2 SETB F0 ;SET F0 INDICATING NO OUTPUT TO DO SETB F1 ;SET fDIFFERENCE,INDICATING NO DIFFERENCES JMP BgnScn ;KEYBOARD STATE WILL BE OUTPUT ; ; HERE AT SOFTWARE RESET COMMAND. ; START0: CLR fEcho ;RESET fECHO MOV fLED,#00H ;TURN ALL THE LED'S OFF IN BITMAP MOV P2,fLED ;AND AT PORT 2 SETB F0 ;SET F0 INDICATING NO OUTPUT TO DO CLR F1 ;RESET fDIFFERENCE, SO THAT THE INITIAL JMP BgnScn ;KEYBOARD STATE WILL BE OUTPUT MAIN: SETB F1 ;SET F1, INDICATING NO DIFFERENCES FOUND YET BgnScn: MOV HysPtr,#rgFHys ;Load hysteresis ptr with addr of beg of table MOV BitMsk,#01H ;Initialize the hysteresis table bit mask CLR A MOV CurAdd,A ;Set the CurAdd register to starting address ; XRL A,#0CH ;Invert bits 2 and 3 to compensate for hardware MOV P0,A ;Output the key address to the matrix hardware MOV A,BitMsk ;Scan2 expects to find the bit mask in A JMP Scan2 Scan: INC CurAdd ;Get the next address MOV A,CurAdd ; JB ACC.3,SCAN3 ;Jump if col ADD IS 8 OR LARGER JB ACC.7,SCAN4 ;Jump if ROW ADD IS 8 OR LARGER Scan1: ; XRL A,#0CH ;Invert bits 2 and 3 to conpensate for hardware MOV P0,A ;Output the key address to the matrix hardware MOV A,BitMsk ;Get the hysteresis table bit mask RL A ;Rotate bit mask into next position MOV BitMsk,A ;Save bit mask for next time JB ACC.0,IncPtr ;Jump if it's been rotated around all the way ;And check for input byte. Scan2: ANL A,@HysPtr ;Get the hysteresis bit JNZ ToWas ;See if the key was depressed SETB P1.2 ;It wasn't, so just output phase 1 SETB P1.0 ;AND SSE NOP ;Must delay before reading key JB T1,BackA JMP PosDep ;test for a possible key depression ToWas: JMP WasDep ;jmp too far for one byte BackA: CLR P1.2 ;If no depression, then just clear phase 1 CLR P1.0 ;AND SSE JMP Scan ;And go to the top of the loop ;SCAN3: JB ACC.1,FIXADD ;JUMP IF COL ADD IS 10 ; JMP SCAN1 ;less than 10,continue ; ;FIXADD: ADD A,#06H ;INC ROW ADD AND SET COL ADD TO ZERO ; MOV CurAdd,A ;SAVE FIXED ADDRESS ; JB ACC.7,SCAN4 ;JUMP IF ROW ADD IS 8 OR LARGER ; JMP SCAN1 ;CONTINUE IF NOT SCAN4: JB ACC.5,TOENDS ;IF ROW IS 10, END OF SCAN JMP SCAN1 ;IF NOT, CONTINUE ; IncPtr: INC HysPtr ;Point to next byte in hysteresis table INBYTE: MOV A,#Empty XCH A,Inbyt ;GET INPUT BYTE, CLEAR IT FOR NEXT TIME, MOV TempIn,A ;AND PROTECT IT FROM INTERUPT ROUTINE CJNE A,#Empty,Inbyt0 ;JUMP IF INPUT BYTE REGISTER IS NOT EMPTY MOV A,BitMsk ;Scan2 expects bitmsk in A JMP Scan2 Inbyt0: CJNE A,#Rset,INBYT9 ;JUMP IF NOT A Reset COMMAND JB WS?,RESPB MOV A,LEDAPT CJNE A,DARGPT,SET_UP ;IS LAST BUFFER ENTRY VISIBLE? JMP START0 ;YES. TURN OFF LEDs & Echo, BEGIN SCAN AS IN STARTUP SET_UP: CALL SETMSB ;NO. ALSO SET MSB OF THE LAST BUFFER ENTRY... CALL UPDATE ;...AND UPDATE THE INTERUPT LEVEL BUFFER PUTTER JMP START0 ;TURN OFF LEDs & Echo, BEGIN SCAN AS IN STARTUP ; RESPB: MOV A,LEDBPT CJNE A,DBRGPT,SET_UP JMP START0 TOENDS: JMP ENDSCN ;ENDSCN is too far away for a 1-byte jump instruction INBYT9: CJNE A,#CheckRom,InbytA ; ; CALL CHKSUM ;COMPUTE THE CHECKSUM OF THE ROM JB WS?,IN9A PUT9: CALL PUTA ;AND PUT IT INTO THE OUTPUT BUFFER CALL UPDATE ;MAKE IT VISIBLE MOV A,BitMsk ;Scan2 expects BitMsk in A JMP SCAN2 ; IN9A: CLR ES JBC NOSERO,IN9B CALL PUTB CALL UPDATE MOV A,BitMsk JMP SCAN2 ; IN9B: MOV SBUF,A SETB ES MOV A,BitMsk JMP SCAN2 ; INBYTA: CJNE A,#HAND,INBYT1 ;LEFT/RIGHT HANDEDNESS? JB WS?,INA1 CLR A ;SINCE LEFT HAND, SEND 00H CALL PUTA CALL UPDATE MOV A,BitMsk JMP SCAN2 INA1: MOV A,#0FFH ;SINCE RIGHT HAND, SEND 0FFH JMP IN9A ; ; CTOS will be sending LED On/Off commands with the following format: ; Bit0 Bit1 Bit2 Bit3 ; LED03: F10 F9 F8 F3 ; LED47: F2 F1 LOCK OVERTYPE ; ; The Cortron kbd is setup to address the LED's in this format: ; ; LED03: F9 F10 F3 F8 ; LED47: F2 F1 OVTYP LOCK ; ; Therefore a certain amount of translation between CTOS commands ; and Cortron addressing is required, as follows: ; ; INBYT1: ANL A,#0F0H CJNE A,#LED03,Inbyt7 ;JUMP IF THE INPUT BYTE IS NOT AN LED03 COMMAND MOV A,TempIn ;GET INPUT BYTE ; ; ABIT0: JB ACC.0,ABIT01 ;IF BIT0 IS 0, CLR fLED.1 ;CLEAR BIT 1 OF LED PATTERN JMP ABIT1 ABIT01: SETB fLED.1 ;ELSE: SET BIT 1 OF LED PATTERN ; ; ABIT1: JB ACC.1,ABIT11 ;IF BIT1 IS 0, CLR fLED.0 ;CLEAR BIT 0 OF LED BIT PATTERN JMP ABIT2 ABIT11: SETB fLED.0 ;ELSE: SET BIT 1 OF LED BIT PATTERN ; ; ABIT2: JB ACC.2,ABIT21 ;IF BIT2 IS 0, CLR fLED.3 ;CLEAR BIT 3 OF LED BIT PATTERN JMP ABIT3 ABIT21: SETB fLED.3 ;ELSE: SET BIT 3 OF LED BIT PATTERN ; ; ABIT3: JB ACC.3,ABIT31 ;IF BIT3 IS 0, CLR fLED.2 ;CLEAR BIT 2 OF LED BIT PATTERN JMP INBYT8 ABIT31: SETB fLED.2 ;ELSE: SET BIT 2 OF LED BIT PATTERN JMP INBYT8 INBYT7: MOV A,TempIn ANL A,#0F0H CJNE A,#LED47,INBYT2 ;JUMP IF THE INPUT BYTE IS NOT AN LED47 COMMAND MOV A,TempIn ;GET INPUT BYTE ; ; BBIT0: JB ACC.0,BBIT01 ;IF BIT0 IS 0, CLR fLED.4 ;CLEAR BIT 4 OF LED BIT PATTERN JMP BBIT1 BBIT01: SETB fLED.4 ;ELSE: SET BIT 4 OF LED BIT PATTERN ; ; BBIT1: JB ACC.1,BBIT11 ;IF BIT1 IS 0, CLR fLED.5 ;CLEAR BIT 5 OF LED BIT PATTERN JMP BBIT2 BBIT11: SETB fLED.5 ;ELSE: SET BIT 5 OF LED BIT PATTERN ; ; BBIT2: JB ACC.2,BBIT21 ;IF BIT2 IS 0, CLR fLED.7 ;CLEAR BIT 7 OF LED BIT PATTERN JMP BBIT3 BBIT21: SETB fLED.7 ;ELSE: SET BIT 7 OF LED BIT PATTERN ; ; BBIT3: JB ACC.3,BBIT31 ;IF BIT3 IS 0, CLR fLED.6 ;CLEAR BIT 6 OF LED BIT PATTERN JMP INBYT8 BBIT31: SETB fLED.6 ;ELSE: SET BIT 6 OF LED BIT PATTERN ; ; ; INBYT8: MOV P2,fLED ;AND OUTPUT LED DATA MOV A,BitMsk ;Scan2 expects BitMsk in A JMP SCAN2 INBYT2: JNB fEcho,INBYT4 ;JUMP IF NOT IN ECHO MODE MOV A,TempIn ;GET INPUT BYTE JB WS?,IN4A CALL PUTA ;AND PUT IT INTO THE OUTPUT BUFFER CALL UPDATE ;MAKE IT VISIBLE MOV A,BitMsk ;Scan2 expects BitMsk in A JMP SCAN2 ; IN4A: CLR ES JBC NOSERO,IN4B IN4A1: CALL PUTB CALL UPDATE MOV A,BitMsk ;Scan2 expects BitMsk in A JMP SCAN2 IN4B: PUSH ACC MOV A,DBRGTK CJNE A,DBRGPT,IN4B1 POP ACC MOV SBUF,A SETB ES MOV A,BitMsk JMP SCAN2 IN4B1: SETB NOSERO POP ACC CALL PUTB CALL UPDATE MOV A,BitMsk ;Scan2 expects BitMsk in A JMP SCAN2 INBYT4: CLR fEcho ;RESET fECHO CJNE TempIn,#ECHO,NOECH ;JUMP IF INPUT BYTE IS NOT THE ECHO COMMAND SETB fEcho ;SET fECHO MOV A,BitMsk ;Scan2 expects BitMsk in A JMP Scan2 ;And continue NOECH: MOV A,BitMsk ;Scan2 expects BitMsk in A JMP Scan2 ;And continue WasDep: JB F0,NOOUT ;IF NO OUTPUT, DON'T OUTPUT SETB KSDOWN MOV A,CurAdd MOV DPTR,#0F00H ;TRANSLATE CURRENT ADDRESS, MOVC A,@A+DPTR ;TO KEYBOARD CODE XCH A,LSTKBD ;EXCHANGE PRESENT KBD CODE WITH LAST KBD CODE CJNE A,#EMPTY,WAS1 ;IF LAST CODE IS PRESENT, PUTX IT. JMP NOOUT ;IF THIS IS THE FIRST KBD CODE THIS SCAN, ; DON'T PUTX ANYTHING. WAS1: JB WS?,WASB CALL PUTA JMP NOOUT WASB: CALL PUTB NOOUT: CLR P1.1 ;Output hysteresis signal SETB P1.2 ;And phase 1 SETB P1.0 ;AND SSE NOP ;Must delay before reading key JB T1,PosRel ;Test for key release BackB: SETB P1.1 ;Clear hysteresis signal JMP BackA PosRel: CLR P1.2 ;Clear phase 1 CLR P1.0 ;AND SSE CALL Static ;Wait for a possible noise spike to settle SETB P1.2 ;Output phase 1 again SETB P1.0 ;AND SSE NOP ;Must delay before reading key JB T1,Releas ;Test key again for release JMP BackB Releas: CLR P1.2 ;Clear phase 1 SETB P1.1 ;Clear hysteresis signal CLR P1.0 ;CLEAR SSE MOV A,BitMsk ;Get bit mask back CPL A ;Complement bit mask ANL A,@HysPtr ;Clear hysteresis bit MOV @HysPtr,A ;And restore to hysteresis bitmap CLR F1 ;Difference was found JMP Scan ;go to the top of the loop PosDep: CLR P1.2 ;Clear phase 1 CLR P1.0 ;AND SSE CALL Static ;Wait for possible noise spike to settle SETB P1.2 ;Output phase 1 again SETB P1.0 ;AND SSE NOP ;Must delay before reading key JNB T1,FstDep ;Test key again, jump if still down JMP BackA ;It was noise, go to 'Back A' to clear phase 1 FstDep: CLR P1.2 ;Clear Phase 1 CLR P1.0 ;AND SSE MOV A,BitMsk ;Get bit mask back ORL A,@HysPtr ;Set hysteresis bit MOV @HysPtr,A ;And restore to hysteresis bitmap CLR F1 ;Difference was found JMP Scan ;And go to the top of the loop ENDSCN: JB F0,FINOU1 ;JUMP IF NOT OUTPUTING JBC KSDOWN,FINOU2 ;JUMP IF ANY KEYS WERE DOWN MOV A,#0C0H JB WS?,ENSCN1 CALPUT: CALL PUTA ;SEND NULL ikey (40H WITH fLast BIT) JMP FINOU4 ; ENSCN1: CLR ES ;TEMP STOP SERIO INTERRUPT JBC NOSERO,ENSCN2 ;IF SERIO IS INACTIVE, JMP. CALL PUTB ;IF NOT: PROCEED AS NORMAL JMP FINOU4 ; ENSCN2: JNB INPASS,ENSCN3 ;JUMP IF ALREADY IN 'RESET-INBOUND-PASS' SETB NOSERO CALL PUTB ;MUST SEND TWO CONTIGUOUS BYTES. JMP FINOU4 ENSCN3: MOV SBUF,A ;IF SERIO INACTIVE: INITIATE TRANSMITION ; DIRECTLY AND AVOID CALLING UPDATE. SETB ES JMP FINOU1 ; FINOU6: CLR ES CALL SETMSB CALL UPDATE JBC NOSERO,FINOU8 SETB ES JMP FINOU1 ; FINOU8: SETB ES MOV A,R0 PUSH ACC MOV R0,DBRGTK MOV SBUF,@R0 INC R0 MOV DBRGTK,R0 CJNE R0,#LIMRGOUTB,FINOU7 MOV DBRGTK,#RGOUTB FINOU7: POP ACC MOV R0,ACC JMP FINOU1 FINOU2: JB WS?,FINOU6 CALL SETMSB ;SET HIGH ORDER BIT OF THE LAST BUFFER ENTRY FINOU4: CALL UPDATE ;UPDATE PUTTER FOR TINTR ROUTINE FINOU1: MOV A,#0FAH ; USING 0 ;WE WANT TO GET AT THE INPUT TINTR COUNTER CLR PSW.4 CLR PSW.3 CLR EX0 ;AN EXT INT AT THIS TIME WOULD BE EMBARASSING ADD A,R3 ;IF A > 5, A CARRY WILL OCCUR JNC FINOU5 ;JUMP IF IN THE MIDST OF INPUTTING: 0 < ITC < 6 MOV R3,#0FFH ;OTHERWISE, LOAD ITC WITH A BIG NUMBER TO KEEP ;THE INPUTTING PART OF THE TINTR ROUTINE FROM ;BEING RUN UNTIL AN INPUT BYTE IS RECEIVED SETB EX0 FINOU5: ; USING 1 CLR PSW.4 SETB PSW.3 JB F1,NODIF CLR F0 ;SET fOUTPUT IF ANY DIFFERENCES DETECTED JMP MAIN ;AND START SCAN WITH OUTPUT NODIF: SETB F0 ;CLEAR fOUTPUT IF NONE DETECTED JMP MAIN ;AND START VANILLA SCAN ;Subroutines BDelay: MOV TempR1,#0 ;Subroutine to delay for about 1/3 second ;Before startup to allow reset glitches to BDela1: MOV DlyCnt,#0 ;Settle before initiating handshaking. BDela2: DJNZ DlyCnt,BDela2 DJNZ TempR1,BDela1 RET Static: MOV DlyCnt,#Delay ;Set delay for static protection Hold: DJNZ DlyCnt,Hold ;Delay to allow any noise spikes to settle RET ;Subroutine to put contents of A (key code, checksum, Echo data) into ; the ring buffer for transmission to the OS at the interrupt level. PUTA: CLR LOCK ;ENTERING CRITICAL SECTION. ANY TIMER OR ;SERIO INTERRUPT MUST WAIT CLR ES ;DISABLE SERIO TEMPORARILY JNB INPASS,PUTA1 ;JMP IF ALREADY IN 'RESET INBOUND PASS-THRU MODE PUSH ACC ;SAVE KBD CODE CLR INPASS ;SET MODE TO 'RESET INBOUND PASS-THRU' SETB DOTWO ;FLAG TO HOLD OFF RECEIVE INTERRUPTS MOV A,#RINPAS ;RESET INBOUND PASS-THRU CMD MOV BUFIN,LEDAPT ;LOAD BUFIN WITH PORT A DATA PUTTER MOV @Bufin,A ;STORE DATA IN DATA A RING BUFFER MOV DARGPT,Bufin ;GIVE UNINC'D PUTTER TO INT ROUTINE INC LEDAPT ;BUMP TO NEXT POSITION MOV A,LEDAPT CJNE A,#LimRgOutA,PUTA6 ;CHECK IF PAST END & JUMP IF NOT MOV LEDAPT,#RgOutA PUTA6: SETB LOCK ;ALLOW THE INTS SETB ES PUTA5: JBC DOTIME,PUTA2 ;IF TIMER IS PENDING, GO CALL IT PUTA3: MOV A,LEDAPT CJNE A,DARGTK,PUTA4 JMP PUTA3 PUTA2: CALL TINTR JMP PUTA3 PUTA4: CLR LOCK CLR ES POP ACC ;GET KBD CODE MOV BUFIN,LEDAPT ;LOAD BUFIN WITH PORT A DATA PUTTER MOV @Bufin,A ;STORE DATA IN DATA A RING BUFFER MOV DARGPT,Bufin ;GIVE UNINC'D PUTTER TO INT ROUTINE INC LEDAPT ;BUMP TO NEXT POSITION MOV A,LEDAPT CJNE A,#LimRgOutA,PUTA7 ;CHECK IF PAST END & JUMP IF NOT MOV LEDAPT,#RgOutA PUTA7: SETB LOCK SETB ES JBC DOTIME,PUTA9 ;IF TIMER IS PENDING, GO CALL IT PUTA8: MOV A,LEDAPT CJNE A,DARGTK,ACHRET JMP PUTA8 ;WAIT FOR TRANSMIT INTERRUPTS BUT HOLD ; OFF RECEIVE INTERRUPTS. PUTA9: CALL TINTR JMP PUTA8 ; ACHRET: CLR DOTWO ;ALLOW RECEIVE INTERRUPTS JBC DINAA,DOAA ;JUMP IF NEED TO TRANS. PORT A TO PORT A ; PT. DEV. IDCODE. JBC DINBA,DOBA ;JUMP IF NEED TO TRANS. PORT B TO PORT A ; (PT DEV TO WS DATA). RET DOAA: MOV DPTR,#TIDSNA PUSH DPL PUSH DPH RET DOBA: MOV DPTR,#SCALPA PUSH DPL PUSH DPH RET ; ; END UP HERE IF ALREADY IN 'RESET INBOUND PASS-THRU' MODE ; AND DON'T NEED TO TRANSMIT 2 CONTIGUOUS BYTES. ; PUTA1: MOV BUFIN,LEDAPT ;LOAD BUFIN WITH PORT A DATA PUTTER MOV @Bufin,A ;STORE DATA IN DATA A RING BUFFER MOV DARGPT,Bufin ;GIVE UNINC'D PUTTER TO INT ROUTINE INC LEDAPT ;BUMP TO NEXT POSITION MOV A,LEDAPT SETB DOTWO ;FLAG TO HOLD OFF RECEIVE INTERRUPTS CJNE A,#LimRgOutA,APUT ;CHECK IF PAST END & JUMP IF NOT MOV LEDAPT,#RgOutA APUT: SETB LOCK ;RELINQUISH CONTROL OF SHARED DATA SETB ES ;ENABLE SERIO INTERRUPT JBC DOTIME,APUT3 ;IF TIMER IS PENDING,GO CALL IT. APUT2: MOV A,LEDAPT ;DATA A BUFFER PUTTER TO A CJNE A,DARGTK,ARET1 ;RETURN IF BUFFER IS NOT FULL JMP APUT2 ;STAY UNTIL THERE IS ROOM ; APUT3: CALL TINTR JMP APUT2 ARET1: CLR DOTWO ;ALLOW RECEIVE INTERRUPTS JBC DINAA,DOAA2 ;JUMP IF NEED TO TRANS. PORT A TO PORT A ; PT. DEV. IDCODE. JBC DINBA,DOBA2 ;JUMP IF NEED TO TRANS. PORT B TO PORT A ; (PT DEV TO WS DATA). RET DOAA2: MOV DPTR,#TIDSNA PUSH DPL PUSH DPH RET DOBA2: MOV DPTR,#SCALPA PUSH DPL PUSH DPH RET ; ; ROUTINE TO PASS ANY DATA TO PORT A REGARDLESS ; OF PASS-THRU MODE. (I.E. ANYTHING OTHER THAN KBD DATA) RPUTA: CLR LOCK ;ENTERING CRITICAL SECTION. ANY TIMER OR ;SERIO INTERRUPT MUST WAIT CLR ES ;DISABLE SERIO TEMPORARILY MOV BUFIN,LEDAPT ;LOAD BUFIN WITH PORT A DATA PUTTER MOV @Bufin,A ;STORE DATA IN DATA A RING BUFFER INC LEDAPT ;BUMP TO NEXT POSITION MOV A,LEDAPT CJNE A,#LimRgOutA,RAPUT ;CHECK IF PAST END & JUMP IF NOT MOV LEDAPT,#RgOutA RAPUT: SETB LOCK ;RELINQUISH CONTROL OF SHARED DATA SETB ES ;ENABLE SERIO INTERRUPT JBC DOTIME,RAPUT3 ;IF TIMER IS PENDING,GO CALL IT. RAPUT2: MOV A,LEDAPT ;DATA A BUFFER PUTTER TO A CJNE A,DARGTK,ARET1A ;IF BUFFER IS NOT FULL, RET SETB FULBUF ;INDICATE TO RECEIPT ROUTINES A FULL BUFFER JMP RAPUT2 ;STAY UNTIL THERE IS ROOM ; RAPUT3: CALL TINTR JMP RAPUT2 ARET1A: MOV DARGPT,LEDAPT ;GIVE INC'D PUTTER TO INT ROUTINE CLR FULBUF ;OK TO SEND PT DEV DATA RET ; ; ROUTINE TO LOAD OUTPUT BUFFER GOING ; TO PORT B (HARD UART) FROM THE KBD. ; PUTB1A: JMP PUTB1 ; PUTB: CLR LOCK ;ENTERING CRITICAL SECTION. ANY TIMER OR ;SERIO INTERRUPT MUST WAIT CLR ES ;DISABLE SERIO TEMPORARILY JNB INPASS,PUTB1A ;JMP IF ALREADY IN 'RESET INBOUND PASS-THRU MODE PUSH ACC ;SAVE KBD CODE CLR INPASS ;SET MODE TO 'RESET INBOUND PASS-THRU' SETB DOTWO ;FLAG TO HOLD OFF RECEIVE INTERRUPTS MOV A,#RINPAS ;RESET INBOUND PASS-THRU CMD MOV BUFIN,LEDBPT ;LOAD BUFIN WITH PORT B DATA PUTTER JBC NOSERO,TXDB ;IF SERIAL OUTPUT INACTIVE, ;SET SERIAL OUTPUT TO ACTIVE, ;AND INITIATE A TRANSMIT DIRECTLY. ; ;IF SERIAL OUTPUT IS ACTIVE (NOSERO=0), ;PUT DATA INTO BUFFER. PUTB6: MOV @Bufin,A ;STORE DATA IN DATA B RING BUFFER MOV DBRGPT,Bufin ;GIVE UNINC'D PUTTER TO INT ROUTINE INC LEDBPT ;BUMP TO NEXT POSITION MOV A,LEDBPT CJNE A,#LimRgOutB,BPUTB1 ;CHECK IF PAST END & JUMP IF NOT MOV LEDBPT,#RgOutB BPUTB1: SETB LOCK ;RELINQUISH CONTROL OF SHARED DATA SETB ES JBC DOTIME,PUTB3 ;IF TIMER IS PENDING, GO CALL IT. PUTB2: MOV A,LEDBPT ;DATA B BUFFER PUTTER TO A CJNE A,DBRGTK,PUTB8 ;CONTINUE IF BUFFER IS NOT FULL JMP PUTB2 ;STAY UNTIL THERE IS ROOM ; PUTB3: CALL TINTR JMP PUTB2 TXDB: PUSH ACC MOV A,DBRGTK CJNE A,DBRGPT,PUTB5 SETB NOSERO POP ACC JMP PUTB6 ; PUTB5: MOV A,R0 MOV R0,DBRGTK MOV SBUF,@R0 INC R0 MOV DBRGTK,R0 CJNE R0,#LIMRGOUTB,PUTB7 MOV DBRGTK,#RGOUTB PUTB7: MOV R0,A POP ACC JMP PUTB6 PUTB8: CLR LOCK ;ENTERING CRITICAL SECTION. ANY TIMER OR ;SERIO INTERRUPT MUST WAIT CLR ES ;DISABLE SERIO TEMPORARILY POP ACC ;GET KBD CODE MOV BUFIN,LEDBPT ;LOAD BUFIN WITH PORT B DATA PUTTER JBC NOSERO,TXDB1 ;IF SERIAL OUTPUT INACTIVE, ;SET SERIAL OUTPUT TO ACTIVE, ;AND INITIATE A TRANSMIT DIRECTLY. ; ;IF SERIAL OUTPUT IS ACTIVE (NOSERO=0), ;PUT DATA INTO BUFFER. PUTB9: MOV @Bufin,A ;STORE DATA IN DATA B RING BUFFER MOV DBRGPT,Bufin ;GIVE UNINC'D PUTTER TO INT ROUTINE INC LEDBPT ;BUMP TO NEXT POSITION MOV A,LEDBPT CJNE A,#LimRgOutB,PUTB10 ;CHECK IF PAST END & JUMP IF NOT MOV LEDBPT,#RgOutB PUTB10: SETB LOCK ;RELINQUISH CONTROL OF SHARED DATA SETB ES JBC DOTIME,PUTB12 ;IF TIMER IS PENDING, GO CALL IT. PUTB11: MOV A,LEDBPT ;DATA B BUFFER PUTTER TO A CJNE A,DBRGTK,BCHRET ;CONTINUE IF BUFFER IS NOT FULL JMP PUTB11 ;STAY UNTIL THERE IS ROOM ; PUTB12: CALL TINTR JMP PUTB11 TXDB1: PUSH ACC MOV A,DBRGTK CJNE A,DBRGPT,PUTB13 SETB NOSERO POP ACC JMP PUTB9 ; PUTB13: MOV A,R0 MOV R0,DBRGTK MOV SBUF,@R0 INC R0 MOV DBRGTK,R0 CJNE R0,#LIMRGOUTB,PUTB14 MOV DBRGTK,#RGOUTB PUTB14: MOV R0,A POP ACC JMP PUTB9 ; BCHRET: CLR DOTWO ;ALLOW RECEIVE INTERUPTS JBC DINBB,DOBB ;RET TO PORT B TO PORT B ; (WS TO WS) ROUTINE JBC DINAB,DOAB ;RET TO PORT A TO PORT B ; (PT DEV TO WS) ROUTINE RET DOBB: MOV DPTR,#SIDSNB PUSH DPL PUSH DPH RET DOAB: MOV DPTR,#TCLPAB PUSH DPL PUSH DPH RET PUTB1: MOV BUFIN,LEDBPT ;LOAD BUFIN WITH PORT B DATA PUTTER JBC NOSERO,INITXD ;IF SERIAL OUTPUT INACTIVE, ;SET SERIAL OUTPUT TO ACTIVE, ;AND INITIATE A TRANSMIT DIRECTLY. ; ;IF SERIAL OUTPUT IS ACTIVE (NOSERO=0), ;PUT DATA INTO BUFFER. BPUT6: MOV @Bufin,A ;STORE DATA IN DATA B RING BUFFER MOV DBRGPT,Bufin ;GIVE UNINC'D PUTTER TO INT ROUTINE INC LEDBPT ;BUMP TO NEXT POSITION MOV A,LEDBPT SETB DOTWO ;DISALLOW RECEIVE INTERUPTS CJNE A,#LimRgOutB,BPUT ;CHECK IF PAST END & JUMP IF NOT MOV LEDBPT,#RgOutB BPUT: SETB LOCK ;RELINQUISH CONTROL OF SHARED DATA SETB ES JBC DOTIME,BPUT3 ;IF TIMER IS PENDING, GO CALL IT. BPUT2: MOV A,LEDBPT ;DATA B BUFFER PUTTER TO A CJNE A,DBRGTK,PUTRET ;RETURN IF BUFFER IS NOT FULL JMP BPUT2 ;STAY UNTIL THERE IS ROOM ; BPUT3: CALL TINTR JMP BPUT2 INITXD: PUSH ACC MOV A,DBRGTK CJNE A,DBRGPT,BPUT5 SETB NOSERO POP ACC JMP BPUT6 ; BPUT5: MOV A,R0 MOV R0,DBRGTK MOV SBUF,@R0 INC R0 MOV DBRGTK,R0 CJNE R0,#LIMRGOUTB,BPUT7 MOV DBRGTK,#RGOUTB BPUT7: MOV R0,A POP ACC JMP BPUT6 PUTRET: CLR DOTWO ;ALLOW RECEIVE INTERUPTS JBC DINBB,DOBB2 ;RET TO PORT B TO PORT B ; (WS TO WS) ROUTINE JBC DINAB,DOAB2 ;RET TO PORT A TO PORT B ; (PT DEV TO WS) ROUTINE RET DOBB2: MOV DPTR,#SIDSNB PUSH DPL PUSH DPH RET DOAB2: MOV DPTR,#TCLPAB PUSH DPL PUSH DPH RET ; ; RPUTB: CLR LOCK ;ENTERING CRITICAL SECTION. ANY TIMER OR ;SERIO INTERRUPT MUST WAIT CLR ES ;DISABLE SERIO TEMPORARILY MOV BUFIN,LEDBPT ;LOAD BUFIN WITH PORT B DATA PUTTER JBC NOSERO,RINIXD ;IF SERIAL OUTPUT INACTIVE, ;SET SERIAL OUTPUT TO ACTIVE, ;AND INITIATE A TRANSMIT DIRECTLY. ; ;IF SERIAL OUTPUT IS ACTIVE (NOSERO=0), ;PUT DATA INTO BUFFER. RBPUT6: MOV @Bufin,A ;STORE DATA IN DATA B RING BUFFER INC LEDBPT ;BUMP TO NEXT POSITION MOV A,LEDBPT CJNE A,#LimRgOutB,RBPUT ;CHECK IF PAST END & JUMP IF NOT MOV LEDBPT,#RgOutB RBPUT: SETB LOCK ;RELINQUISH CONTROL OF SHARED DATA SETB ES JBC DOTIME,RBPUT3 ;IF TIMER IS PENDING, GO CALL IT. RBPUT2: MOV A,LEDBPT ;DATA B BUFFER PUTTER TO A CJNE A,DBRGTK,BRETB1 ;RETURN IF BUFFER IS NOT FULL SETB FULBUF ;INDICATE TO RECEIPT ROUTINES A FULL BUFFER JMP RBPUT2 ;STAY UNTIL THERE IS ROOM ; BRETB1: MOV DBRGPT,LEDBPT ;GIVE INC'D PUTTER TO INT ROUTINE CLR FULBUF ;OK TO SEND PT DEV DATA RET ; RBPUT3: CALL TINTR JMP RBPUT2 RINIXD: PUSH ACC MOV A,DBRGTK CJNE A,DBRGPT,RBPUT5 SETB NOSERO POP ACC JMP RBPUT6 ; RBPUT5: MOV A,R0 MOV R0,DBRGTK MOV SBUF,@R0 INC R0 MOV DBRGTK,R0 CJNE R0,#LIMRGOUTB,RBPUT7 MOV DBRGTK,#RGOUTB RBPUT7: MOV R0,A POP ACC JMP RBPUT6 ; ;SUBROUTINE TO UPDATE THE INTERUPT LEVEL BUFFER PUTTER (MAKE IT VISIBLE) UPDATE: JB WS?,UPB MOV DARGPT,LEDAPT ;UPDATE DATA A PUTTER RET UPB: MOV DBRGPT,LEDBPT ;UPDATE DATA B PUTTER RET ;SUBROUTINE TO SET THE HIGH ORDER BIT OF THE LAST ENTRY INTO THE RING BUFFER SETMSB: MOV A,#EMPTY XCH A,LSTKBD ;GET LAST KBD CODE AND CLEAR BUFFER. SETB ACC.7 ;SET THE MSB JB WS?,MSBPB CALL PUTA RET MSBPB: CALL PUTB RET ; ; ; ;SUBROUTINE TO COMPUTE THE CHECKSUM OF THE 8051 ROM CHKSUM: MOV TempR1,#0H ;INITIALIZE CHECKSUM TO ZERO MOV TempIn,#0H ;INITIALIZE CODE INDEX TO ZERO CHKSU1: MOV A,TempIn MOV DPTR,#00H ;GET CODE FROM LOCATION (R6)+0H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE MOV DPTR,#100H MOVC A,@A+DPTR ;GET CODE FROM LOCATION (R6)+100H ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE MOV DPTR,#200H ;GET CODE FROM LOCATION (R6)+200H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE MOV DPTR,#300H ;GET CODE FROM LOCATION (R6)+300H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#400H ;GET CODE FROM LOCATION (R6)+400H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#500H ;GET CODE FROM LOCATION (R6)+500H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#600H ;GET CODE FROM LOCATION (R6)+600H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#700H ;GET CODE FROM LOCATION (R6)+700H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#800H ;GET CODE FROM LOCATION (R6)+800H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#900H ;GET CODE FROM LOCATION (R6)+900H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#0A00H ;GET CODE FROM LOCATION (R6)+0A00H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#0B00H ;GET CODE FROM LOCATION (R6)+0B00H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#0C00H ;GET CODE FROM LOCATION (R6)+0C00H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#0D00H ;GET CODE FROM LOCATION (R6)+0D00H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#0E00H ;GET CODE FROM LOCATION (R6)+0E00H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE ; ; MOV DPTR,#0F00H ;GET CODE FROM LOCATION (R6)+0F00H MOVC A,@A+DPTR ADD A,TempR1 ;ADD CURRENT CODE BYTE TO CHECKSUM MOV TempR1,A ;AND STORE IT MOV A,TempIn ;RECOVER POSITION IN PAGE MOV A,#0FAH ; USING 0 ;WE WANT TO GET AT THE INPUT TINTR COUNTER CLR PSW.4 CLR PSW.3 CLR EX0 ;AN EXT INT AT THIS TIME WOULD BE EMBARASSING ADD A,R3 ;IF A > 5, A CARRY WILL OCCUR JNC CHKSU2 ;JUMP IF IN THE MIDST OF INPUTTING: 0 < ITC < 6 MOV R3,#0FFH ;OTHERWISE, LOAD ITC WITH A BIG NUMBER TO KEEP ;THE INPUTTING PART OF THE TINTR ROUTINE FROM ;BEING RUN UNTIL AN INPUT BYTE IS RECEIVED SETB EX0 CHKSU2: ; USING 1 CLR PSW.4 SETB PSW.3 DJNZ TempIn,CHKS1 ;GO TO NEXT LOCATION IN EACH PAGE MOV A,TempR1 ;IF DONE, MOVE CHECKSUM INTO A RET CHKS1: JMP CHKSU1 ;EXTERNAL INTERUPT ROUTINE (ACTIVATED ON RECEIPT OF START BIT) EXTINT: PUSH PSW PUSH ACC ; USING 0 CLR PSW.4 CLR PSW.3 MOV R4,#10 ;INITIALIZE INPUT SHIFT COUNTER MOV R7,#0H ;CLEAR INPUT BYTE MOV R3,#3 ;LOAD INPUT TINTR CNTR SO AS TO CENTER STRT BIT MOV A,R5 XRL A,R3 ;IS THE INPUT GOING TO COLLIDE WITH THE OUTPUT? JNZ EXTIN1 ;NO INC R3 ;YES. SKEW INPUT SAMPLING SO THAT THEY DON'T EXTIN1: CLR EX0 ;DISABLE FURTHER EXTERNAL INTERUPTS POP ACC ;UNTIL DONE WITH THIS INPUT BYTE POP PSW RETI ;TIMER INTERRUPT ROUTINE (ACTIVATED EVERY .164 msec) ; ; CAUTION: THE EXECUTION TIME OF THIS INTERUPT ROUTINE IS VERY CRITICAL. IN ; THE WORST CASE WHERE THE INPUT AND AN OUTPUT ROUTINE ARE ACTIVE ; SIMULTANEOUSLY, THE ROUTINE IS NOT, IN FACT, ABLE TO PERFORM ITS ; TASK IN THE INTERVAL ALLOTTED BETWEEN TIMER INTERUPTS, LEADING ; TO CUMULATIVE ERRORS IN THE TIME AT WHICH INCOMING WAVEFORMS ; ARE SAMPLED AND AT WHICH OUTGOING SIGNAL LEVELS ARE CHANGED. ; THUS, BE SURE THAT YOU KNOW WHAT YOU'RE DOING BEFORE YOU MESS ; AROUND WITH THE CODE. TINTR: PUSH PSW PUSH ACC ; USING 0 CLR PSW.4 CLR PSW.3 MOV R0,DARGPT MOV R1,DARGTK DJNZ R3,OTAPUT ;JUMP IF INPUT TINTR COUNTER NOT YET ZERO MOV R3,#5 ;RELOAD INPUT TINTR COUNTER DJNZ R4,INA2A ;JUMP IF IN THE MIDDLE OF AN INPUT BYTE JNB P3.2,IN1 ;THIS BIT SHOULD BE A ONE (STOP BIT) MOV R3,#0FFH ;THIS IS THE STOP BIT--SET R3 TO A BIG NUMBER SETB EX0 ;(MEANING THAT THERE IS NO INPUT IN PROGRESS), MOV A,R7 ;GET JUST RECEIVED BYTE ; ; CHECK TO SEE IF PORTS HAVE BEEN ID'D YET ; JB SETOK,TAT1 ;JUMP IF PORTS HAVE BEEN ID'D JB GTSYNC,TTA1 ;JUMP IF RECEIVED 2 IDSYNC BYTES JBC ONSYNC,TTA2 ;JUMP IF RECEIVED 1 IDSYNC BYTE CJNE A,#IDSYNC,TT2 ;IF NOT AN IDSYNC, KEEP CHECKING. SETB ONSYNC ;IT IS THE FIRST IDSYNC BYTE. MARK IT, JMP OUTPUT ; TAT1: JMP TT1 ; IN1: MOV R3,#0FFH ;STOP BIT WAS NOT CORRECT! DO NOT SEND ;JUST-RECEIVED BYTE TO BACKGROUND ROUTINE. SETB EX0 JMP OUTPUT TTA2: CJNE A,#IDSYNC,TT2 ;2ND IDSYNC BYTE? SETB GTSYNC ;YUP: MARK IT AND WAIT FOR IDCODE JMP OUTPUT TTA1: CJNE A,#IDSYNC,TTA3 ;IF NOT IDSYNC BYTE, IT'S IDCODE JMP OUTPUT ;IF IS IDSYNC BYTE, WAIT 'TILL IT'S NOT TTA3: JBC CODE2,TTA4 ;1ST OR 2ND IDCODE BYTE? MOV IDCOD1,A ;THIS IS THIS FIRST SETB CODE2 JMP OUTPUT TTA4: MOV IDCOD2,A ;THIS IS THE 2ND CLR GTSYNC SETB SETOK SETB WS? ;THIS IS PT DEV PORT. OS TALKS ;THROUGH PORT B (WS?=1) OTAPUT: JMP OUTPUT INA2A: JMP INPUT2 TT2: CJNE A,#RSET,TT3 ;RESET COMMAND? CLR WS? ;THIS IS THE WORKSTATION PORT SETB SETOK JMP TT4 ; TT3: CJNE A,#CHECKROM,TT5 ;CHECKSUM COMMAND? CLR WS? ;THIS IS THE WORKSTATION PORT SETB SETOK JMP TT4 TT5: CJNE A,#ECHO,TT6 ;ECHO COMMAND? CLR WS? ;THIS IS THE WORKSTATION PORT SETB SETOK JMP TT4 TT6: ANL A,#0F0H CJNE A,#LED03,TT7 ;LED COMMAND? CLR WS? ;THIS IS THE WORKSTATION PORT SETB SETOK MOV A,R7 TT4: ; USING 1 CLR PSW.4 SETB PSW.3 MOV Inbyt,A ;STORE INPUT BYTE FOR BACKGROUND ROUTINE ; ; USING 0 CLR PSW.4 CLR PSW.3 JMP OUTPUT ;REENABLE EXTERNAL INTERUPTS TT7: CJNE A,#LED47,TT7A ;LED COMMAND? CLR WS? ;THIS IS THE WORKSTATION PORT SETB SETOK MOV A,R7 JMP TT4 TT7A: CJNE R7,#HAND,TT8 ;LEFT/RIGHT HAND QUERY? CLR WS? SETB SETOK MOV A,R7 JMP TT4 ; TT8: CJNE R7,#PASTHR,TT9 ;SET ALLOW INBOUND PASS-THRU? SETB PSTHRU JMP OUTPUT TT9: CJNE R7,#NOPASS,OTAPUT ;RESET ALLOW PASS-THRU? CLR PSTHRU JMP OUTPUT ; ; THE PORTS HAVE BEEN ID'D. ; TT1: JNB WS?,TTA ;JUMP IF THIS IS WORKSTATION PORT ; ; IF THE INBOUND PASS-THRU MODE IS SET (ALLOWING PT DEV DATA TO WS), ; ALL DATA IS PASSED THROUGH. THIS INCLUDES THE CASE WHERE A PT DEV ; IS SENDING AND IS REMOVED FROM THE SYSTEM AND REPLACED BY ANOTHER ; PT DEV. THE NEW PT DEV WILL SEND ITS IDSYNC BYTE STREAM AND IDCODES. ; THE KBD WILL PASS ALL THIS DATA THROUGH. IT IS UP TO THE KBD HANDLER ; IN TO OS TO RECOGNIZE THE SWITCHING OF PT DEV'S. IF, HOWEVER, THE ; INBOUND PASS-THRU MODE IS RESET, THE KBD MUST RECOGNIZE THE START-UP ; OF A NEW PT DEV (AT LEAST 40 IDSYNC BYTES) AND STORE ITS IDCODE FOR ; FUTURE REFERENCE. ; ; IF, IN THE PROCESS OF PASSING PT DEV DATA TO THE WS, THE KBD RECOGNIZES ; A 'RESET INBOUND PASS-THRU' COMMAND IN THE DATA STREAM, THE KBD MUST ; DUPLICATE (SEND IT TWICE) THIS DATA BYTE. ; JB PSTHRU,TTF ;IF SET INPASS-THRU, PASS EVERYTHING JB ONSYNC,TTG ;HAVE RECEIVED AT LEAST 40 IDSYNC BYTES, ; LOOK FOR IDCODE. CJNE A,#IDSYNC,TOOUT ;IS THIS AN IDSYNC BYTE? INC COUNT ;YES: COUNT IT MOV A,COUNT CJNE A,#40,OPUT ;GOT 40 YET? SETB ONSYNC ;YES: MARK IT AND LOOK FOR IDCODES OPUT: JMP OUTPUT TOOUT: CLR A ;NOT AN IDSYNC BYTE: CLEAR ANY THAT CAME BEFORE MOV COUNT,A JMP OUTPUT ; TTF: JNB FULBUF,TTF2 ;CONTINUE IF BUFFER IF IS NOT FULL SETB LOST JMP OUTPUT TTF2: CJNE A,#RINPAS,TTF1 ;IF PT DEV-TO-WS DATA IS EQUAL TO ; THE 'RESET INBOUND PASS-THRU' CMD, ; IT MUST BE TRANSMITTED TWICE TO ; DISTINGUISH IT FROM A REAL KBD-TO-WS ; CMD RESETTING THE INBOUND PASS-THRU. MOV TSCR2,A TTF1: MOV TSCR1,A SETB TDCLAB ;DO PUTBUF AFTER INTERRUPT ROUTINE JMP OUTPUT TTG: CJNE A,#IDSYNC,TTJ ;IF NOT IDSYNC BYTE, IT'S IDCODE JMP OUTPUT ;IF IS IDSYNC BYTE, WAIT 'TILL IT'S NOT TTJ: JBC CODE2,TTK ;1ST OR 2ND IDCODE BYTE? MOV IDCOD1,A ;THIS IS THIS FIRST SETB CODE2 JMP OUTPUT TTK: MOV IDCOD2,A ;THIS IS THE 2ND CLR ONSYNC JMP OUTPUT ; ; TTA: JBC ONERPS,TTA1B ;JUMP IF ALREADY GOT ONE 'RESET OUTPASS-THRU' CJNE A,#RPASOT,TTA1A ;IS THIS A 'RESET OUTBOUND PASS-THRU CMD? SETB ONERPS JMP OUTPUT TTA1B: CJNE A,#RPASOT,TTAA1 ;JUMP IF FIRST 'RESET OUTPAS-THRU' WAS REALLY A CMD. JMP TTS ;IT WAS REALLY PT DEV DATA, SO SEND IT. TTAA1: CLR PASOUT ;CLEAR FLAG TO STOP OUTBOUND PASS-THRU. ;UNTIL RECEIPT OF A 'SET OUTBOUND PASS-THRU ;CMD, ALL INCOMING CMDS WILL BE KBD CMDS. TTA1A: JB PASOUT,TTS ;IF PASOUT IS SET, SEND INPUT BYTE TO PT DEV. CJNE A,#PASTHR,TTB ;IS THIS AN 'ALLOW INPASS-THRU' CMD? MOV TSCR1,IDCOD1 ;YES: SEND POINTING DEVICE CODE TO WS. MOV TSCR2,IDCOD2 MOV IDCOD1,#00 ;AFTER SENDING STORED IDCODES, REPLACE THEM MOV IDCOD2,#NOPTDV ;WITH 'NO DEVICE ATTACHED' CODES SETB IDSDAT ;USE 'ID FROM WS PORT TO WS PORT' ROUTINE JMP OUTPUT TTB: CJNE A,#NOPASS,TTQ ;IS IT A DISALLOW-PASS-THRU CMD? CLR PSTHRU ;YES: CLEAR 'ALLOW INPASS' JMP OUTPUT TTQ: CJNE A,#SPASOT,TT4A ;IS IT A 'SET OUTBOUND PASS-THRU' CMD? SETB PASOUT ;YES: SET THE FLAG. UNTIL RECEIPT OF A ; 'RESET OUTBOUND PASS-THRU' CMD, ANY ; INPUT BYTE WILL GO TO THE PT DEV. JMP OUTPUT ; TT4A: JMP TT4 ; ; IT IS A WS TO PT. DEV. CMD. ; SEND IT THERE... ; TTS: MOV TSCR1,A ;MOV DATA TO TEMP LOCATION SETB TDOCLB ;AND DO PUTB AFTER INTERRRUPT JMP OUTPUT ; INPUT2: MOV A,R7 ;FETCH INPUT BYTE JNB P3.2,INPUT3 ;JUMP IF THE INTERUPT LINE IS LOW (SPACE) INC A ;INSERT A MARK IF INTERUPT LINE IS HIGH INPUT3: RR A MOV R7,A ;PUT INPUT BYTE BACK MOV R2,A ; OUTPUT: DJNZ R5,INTRET ;JUMP IF OUTPUT TINTR COUNTER NOT YET ZERO MOV R5,#5 MOV A,R6 ;OUTPUT SHIFT COUNTER TO 'A' JNZ OUTPT1 ;BRANCH IF ALREADY DOING CHARACTER MOV A,R0 XRL A,R1 ;COMPARE PUTTER AND TAKER JZ INTRET ;BRANCH IF NOTHING TO SEND MOV R6,#9 ;LOAD OUTPUT SHIFT COUNTER MOV A,R5 XRL A,R3 ;COMPARE INPUT AND OUTPUT TINTR COUNTERS JNZ SPACE ;IF UNEQUAL, JUST SEND START BIT INC R5 ;IF EQUAL, ALSO INC OTC SO THAT THE INPUT AND JMP SPACE ;OUTPUT ROUTINES WON'T RUN ON THE SAME TINTR OUTPT1: DJNZ R6,OUTPT2 ;JUMP IF SENDING DATA BITS INC R1 ;HERE IF SENT ALL DATA BITS, BUMP RING PTR MOV A,R1 XRL A,#LimRgOutA JNZ MARK ;JUMP IF NOT AT END MOV R1,#RgOutA ;OTHERWISE RESET PTR TO BEGINNING ;AND IN ANYCASE SEND A STOP BIT MARK: SETB P1.3 ;SET DATA A HIGH (MARK) JMP INTRET ; OUTPT2: MOV A,@R1 ;LOAD CHARACTER RR A ;ROTATE NEXT BIT INTO A7 MOV @R1,A ;PUT CHARACTER BACK JB ACC.7,MARK ;JUMP IF MARK SPACE: CLR P1.3 ;CLEAR DATA A (SPACE) INTRET: MOV DARGPT,R0 MOV DARGTK,R1 POP ACC ;RESTORE ACCUMULATOR POP PSW JBC IDSDAT,CALC JBC TDOCLB,CALB JBC TDCLAB,CALA RETI ;RETURN FROM INTERRUPT CALB: MOV DPTR,#TCALPB ;IN ORDER TO LOAD THE OUTPUT BUFFER OUTSIDE ;OF THE INTERRRUPT TIME, THE STACK WILL BE ;LOADED WITH THE ADDRESS OF A CALL TO THE ;BUFFER LOADING ROUTINE. THIS WAY, WHEN THE ;RETI INSTRUCTION IS EXECUTED FROM THE INTERRUPT ;ROUTINE, INTERRUPTS WILL BE ENABLED AND CONTROL ;WILL TRANSFER TO THE CALL. UPON RETURN FROM THE ;CALL, THE RET INSTRUCTION AFTER THE CALL WILL ;RETURN CONTROL TO THE INTERRUPTED ADDRESS. PUSH DPL PUSH DPH RETI ; CALA: MOV DPTR,#TCLPAB ;IN ORDER TO LOAD THE OUTPUT BUFFER OUTSIDE ;OF THE INTERRRUPT TIME, THE STACK WILL BE ;LOADED WITH THE ADDRESS OF A CALL TO THE ;BUFFER LOADING ROUTINE. THIS WAY, WHEN THE ;RETI INSTRUCTION IS EXECUTED FROM THE INTERRUPT ;ROUTINE, INTERRUPTS WILL BE ENABLED AND CONTROL ;WILL TRANSFER TO THE CALL. UPON RETURN FROM THE ;CALL, THE RET INSTRUCTION AFTER THE CALL WILL ;RETURN CONTROL TO THE INTERRUPTED ADDRESS. PUSH DPL PUSH DPH RETI ; CALC: MOV DPTR,#TIDSNA PUSH DPL PUSH DPH RETI ; ; ; ; ; ; CALL ROUTINES FROM INTERRUPTS ; ; ; THIS ROUTINE USED TO TRANSMIT PT DEV IDCODE ; FROM PORT A TO PORT A ; UPON RECEIPT OF 'ALLOW PASS-THRU' CMD. ; TIDSNA: JNB DOTWO,TSND2 ;CONTINUE IF NOT INTERRUPTING CONTINUITY ;OF KBD DATA. SETB DINAA ;FORCE RETURN TO THIS CODE WHEN KBD FINISHES RET ; ; TSND2: PUSH ACC JB INPASS,TSND1 ;JMP IF ALREADY IN 'SET INBOUND PASS' MODE SETB INPASS ;NOT IN IT, SO SET IT MOV A,#SINPAS JNB LOST,TSND3 CLR LOST INC A TSND3: CALL RPUTA ;SEND 'SET IN PASS MODE' BYTE TSND1: MOV A,#IDSYNC CALL RPUTA ;SEND 1 IDSYNC BYTE MOV A,TSCR1 ;SEND 1ST OF 2 IDCODE BYTES CALL RPUTA MOV A,TSCR2 ;SEND 2ND OF 2 IDCODE BYTES CALL RPUTA CLR A MOV TSCR2,A SETB PSTHRU ;SET PASS-THRU FLAG POP ACC RET ; ; THIS ROUTINE IS USED FOR PT. DEV. TO WS ; (PORT A TO PORT B) TRANSFERS ; TCLPAB: JNB DOTWO,TPAB2 SETB DINAB ;FORCE RET IF INTERRUPTING CONTINUITY ; OF KBD DATA. RET TPAB2: PUSH ACC JB INPASS,TPAB1 ;JUMP IF ALREADY IN 'SET INBOUND PASS-THRU' MODE SETB INPASS ;NOT IN IT, SO SET IT MOV A,#SINPAS JNB LOST,TPAB3 CLR LOST INC A TPAB3: CALL RPUTB JB FULBUF,ENDPAB ;DON'T SEND DATA IF BUFFER IS FULL TPAB1: MOV A,TSCR1 ;GET BYTE TO OUTPUT CALL RPUTB ;PUT IT IN OUTPUT BUFFER MOV A,TSCR2 ;CHECK FOR 2ND TRANSMISSION CJNE A,#00H,TCAL1 ENDPAB: POP ACC ;RESTORE ACC RET ;RETURN TO BACKGROUND ROUTINE TCAL1: CALL RPUTB CLR A MOV TSCR2,A POP ACC RET ; ; THIS ROUTINE IS USED FOR WS TO PT. DEV. ; (PORT A TO PORT B) TRANSFERS. ; THERE IS NO NEED FOR ANY 'PASS-THRU' BYTES. ; THERE WILL ALWAYS BE ONLY ONE BYTE TRANSMITTED AT A TIME. ; TCALPB: PUSH ACC CLR ES MOV A,TSCR1 ;GET BYTE TO OUTPUT JNB NOSERO,TCLPB1 CLR NOSERO MOV SBUF,A POP ACC SETB ES RET TCLPB1: CALL RPUTB ;PUT IT IN OUTPUT BUFFER POP ACC ;RESTORE ACC RET ;RETURN TO BACKGROUND ROUTINE ; ;************************************************* ; SERIAL I/O INTERRUPT ROUTINE (HARD UART) ;************************************************* TRANSA: JMP TRANS ; SERIO: PUSH PSW PUSH ACC ; USING 2 SETB PSW.4 CLR PSW.3 JBC TI,TRANSA ;TRANS OR RECEIVING? CLR RI ;RECEIVING. MOV A,SBUF ;INPUT BYTE TO ACC MOV R1,A ; ; CHECK TO SEE IF PORTS HAVE BEEN ID'D YET ; JB SETOK,SER1A1 ;JUMP IF PORTS HAVE BEEN ID'D JB GTSYNS,SERA1 ;JUMP IF RECEIVED 2 IDSYNC BYTES JBC ONSYNS,SERA2 ;JUMP IF RECEIVED 1 IDSYNC BYTE CJNE A,#IDSYNC,SER2 ;IF NOT AN IDSYNC, KEEP CHECKING. SETB ONSYNS ;IT IS THE FIRST IDSYNC BYTE. MARK IT, JMP SOUT SER1A1: JMP SER1 SERA2: CJNE A,#IDSYNC,SER2 ;2ND IDSYNC BYTE? SETB GTSYNS ;YUP: MARK IT AND WAIT FOR IDCODE JMP SOUT SERA1: CJNE A,#IDSYNC,SERA3 ;IF NOT IDSYNC BYTE, IT'S IDCODE JMP SOUT ;IF IS IDSYNC BYTE, WAIT 'TILL IT'S NOT SERA3: JBC SCODE2,SERA4 ;1ST OR 2ND IDCODE BYTE? MOV IDCOD1,A ;THIS IS THIS FIRST SETB SCODE2 JMP SOUT SERA4: MOV IDCOD2,A ;THIS IS THE 2ND CLR GTSYNC CLR WS? ;YES: THIS IS POINTING DEVICE PORT, ; PORT A TALKS TO WORKSTATION (WS?=0). SETB SETOK JMP SOUT SER2: CJNE A,#RSET,SER3 ;RESET COMMAND? SETB WS? ;THIS IS THE WORKSTATION PORT SETB SETOK JMP SER4 ; SER3: CJNE A,#CHECKROM,SER5 ;CHECKSUM COMMAND? SETB WS? ;THIS IS THE WORKSTATION PORT SETB SETOK JMP SER4 SER5: CJNE A,#ECHO,SER6 ;ECHO COMMAND? SETB WS? ;THIS IS THE WORKSTATION PORT SETB SETOK JMP SER4 SER6: ANL A,#0F0H CJNE A,#LED03,SER7 ;LED COMMAND? SETB WS? ;THIS IS THE WORKSTATION PORT SETB SETOK MOV A,R1 SER4: ; USING 1 CLR PSW.4 SETB PSW.3 MOV Inbyt,A ;STORE INPUT BYTE FOR BACKGROUND ROUTINE ; ; USING 2 SETB PSW.4 CLR PSW.3 SOUTA: JMP SOUT ;REENABLE EXTERNAL INTERUPTS SER7: CJNE A,#LED47,SER7A ;LED COMMAND? SETB WS? ;THIS IS THE WORKSTATION PORT SETB SETOK MOV A,R1 JMP SER4 SER7A: CJNE R1,#HAND,SER8 ;LEFT/RIGHT HAND QUERY? SETB WS? SETB SETOK MOV A,R1 JMP SER4 ; SER8: CJNE R1,#PASTHR,SER9 ;SET ALLOW INBOUND PASS-THRU? SETB PSTHRU JMP SOUT SER9: CJNE R1,#NOPASS,SOUTA ;RESET ALLOW PASS-THRU? CLR PSTHRU JMP SOUT ; THE PORTS HAVE BEEN ID'D. ; SER1: JB WS?,SERA ;JUMP IF THIS IS WORKSTATION PORT ; ; IF THE INBOUND PASS-THRU MODE IS SET (ALLOWING PT DEV DATA TO WS), ; ALL DATA IS PASSED THROUGH. THIS INCLUDES THE CASE WHERE A PT DEV ; IS SENDING AND IS REMOVED FROM THE SYSTEM AND REPLACED BY ANOTHER ; PT DEV. THE NEW PT DEV WILL SEND ITS IDSYNC BYTE STREAM AND IDCODES. ; THE KBD WILL PASS ALL THIS DATA THROUGH. IT IS UP TO THE KBD HANDLER ; IN TO OS TO RECOGNIZE THE SWITCHING OF PT DEV'S. IF, HOWEVER, THE ; INBOUND PASS-THRU MODE IS RESET, THE KBD MUST RECOGNIZE THE START-UP ; OF A NEW PT DEV (AT LEAST 40 IDSYNC BYTES) AND STORE ITS IDCODE FOR ; FUTURE REFERENCE. ; ; IF, IN THE PROCESS OF PASSING PT DEV DATA TO THE WS, THE KBD RECOGNIZES ; A 'RESET INBOUND PASS-THRU' COMMAND IN THE DATA STREAM, THE KBD MUST ; DUPLICATE (SEND IT TWICE) THIS DATA BYTE. ; JB PSTHRU,SERF JB ONSYNS,SERG ;HAVE RECEIVED AT LEAST 40 IDSYNC BYTES, ; LOOK FOR IDCODE. CJNE A,#IDSYNC,STOOUT;IS THIS AN IDSYNC BYTE? INC COUNT ;YES: COUNT IT MOV A,COUNT CJNE A,#40,SOPUT ;GOT 40 YET? SETB ONSYNS ;YES: MARK IT AND LOOK FOR IDCODES SOPUT: JMP SOUT STOOUT: CLR A ; NOT AN IDSYNC BYTE: CLEAR ANY THAT CAME BEFORE MOV COUNT,A JMP SOUT ; SERF: JNB FULBUF,SERF2 ;CONTINUE IF BUFFER IS NOT FULL SETB LOST JMP SOUT SERF2: CJNE A,#RINPAS,SERF1 ;IF PT DEV-TO-WS DATA IS EQUAL TO ; THE 'RESET INBOUND PASS-THRU' CMD, ; IT MUST BE TRANSMITTED TWICE TO ; DISTINGUISH IT FROM A REAL KBD-TO-WS ; CMD RESETTING THE INBOUND PASS-THRU. MOV SESCR2,A SERF1: MOV SESCR1,A SETB SDCLBA ;DO PUTBUF AFTER INTERRUPT ROUTINE JMP SOUT SERG: CJNE A,#IDSYNC,SERJ ;IF NOT IDSYNC BYTE, IT'S IDCODE JMP SOUT ;IF IS IDSYNC BYTE, WAIT 'TILL IT'S NOT SERJ: JBC CODE2,SERK ;1ST OR 2ND IDCODE BYTE? MOV IDCOD1,A ;THIS IS THIS FIRST SETB SCODE2 JMP SOUT SERK: MOV IDCOD2,A ;THIS IS THE 2ND CLR GTSYNS JMP SOUT ; ; SERA: JBC ONERPS,SERA1B ;JUMP IF ALREADY GOT ONE 'RESET OUTPASS-THRU' CJNE A,#RPASOT,SERA1A ;IS THIS A 'RESET OUTBOUND PASS-THRU' CMD? SETB ONERPS JMP SOUT SERA1B: CJNE A,#RPASOT,SERAA1 ;JUMP IF FIRST 'RESET OUTPASS-THRU' WAS REALLY A CMD. JMP SERS ;IT WAS REALLY PT DEV DATA, SO SEND IT. SERAA1: CLR PASOUT ;CLEAR FLAG TO STOP OUTBOUND PASS-THRU. ;UNTIL RECEIPT OF A 'SET OUTBOUND PASS-THRU ;CMD, ALL INCOMING CMDS WILL BE KBD CMDS. SERA1A: JB PASOUT,SERS ;IF PASOUT IS SET, SEND INPUT BYTE TO PT DEV. CJNE A,#PASTHR,SERB ;IS THIS AN 'ALLOW INPASS-THRU' CMD? MOV SESCR1,IDCOD1 ;AND SEND POINTING DEVICE CODE TO WS. MOV SESCR2,IDCOD2 MOV IDCOD1,#00 ;AFTER SENDING STORED IDCODES, REPLACE THEM MOV IDCOD2,#NOPTDV ;WITH 'NO DEVICE ATTACHED' CODES SETB IDSDBT JMP SOUT SERB: CJNE A,#NOPASS,SERQ ;IF NOT A DISALLOW-PASS-THRU CMD, ;CHECK FOR OTHER KBD COMMANDS. CLR PSTHRU JMP SOUT SER4A: JMP SER4 SERQ: CJNE A,#SPASOT,SER4A ;IS IT A 'SET OUTBOUND PASS-THRU' CMD? SETB PASOUT ;YES: SET THE FLAG. UNTIL RECEIPT OF A ; 'RESET OUTBOUND PASS-THRU' CMD, ANY ; INPUT BYTE WILL GO TO THE PT DEV. JMP SOUT ; ; IT IS A WS TO PT. DEV. CMD. ; SEND IT THERE... ; SERS: MOV SESCR1,A ;MOV DATA TO TEMP LOCATION SETB SDOCLA ;AND DO PUTA AFTER INTERRRUPT SOUT: POP ACC POP PSW JBC IDSDBT,SCALC ;B TO B (WS TO WS; PT DEV ID CODE) JBC SDOCLA,SCALB ;B TO A (WS TO PT DEV) JBC SDCLBA,SCALA ;B TO A (PT DEV TO WS) RETI ;RETURN FROM INTERRUPT SCALB: MOV DPTR,#SCALPB ;IN ORDER TO LOAD THE OUTPUT BUFFER OUTSIDE ;OF THE INTERRRUPT TIME, THE STACK WILL BE ;LOADED WITH THE ADDRESS OF A CALL TO THE ;BUFFER LOADING ROUTINE. THIS WAY, WHEN THE ;RETI INSTRUCTION IS EXECUTED FROM THE INTERRUPT ;ROUTINE, INTERRUPTS WILL BE ENABLED AND CONTROL ;WILL TRANSFER TO THE CALL. UPON RETURN FROM THE ;CALL, THE RET INSTRUCTION AFTER THE CALL WILL ;RETURN CONTROL TO THE INTERRUPTED ADDRESS. PUSH DPL PUSH DPH RETI ; SCALA: MOV DPTR,#SCALPA ;IN ORDER TO LOAD THE OUTPUT BUFFER OUTSIDE ;OF THE INTERRRUPT TIME, THE STACK WILL BE ;LOADED WITH THE ADDRESS OF A CALL TO THE ;BUFFER LOADING ROUTINE. THIS WAY, WHEN THE ;RETI INSTRUCTION IS EXECUTED FROM THE INTERRUPT ;ROUTINE, INTERRUPTS WILL BE ENABLED AND CONTROL ;WILL TRANSFER TO THE CALL. UPON RETURN FROM THE ;CALL, THE RET INSTRUCTION AFTER THE CALL WILL ;RETURN CONTROL TO THE INTERRUPTED ADDRESS. PUSH DPL PUSH DPH RETI ; SCALC: MOV DPTR,#SIDSNB PUSH DPL PUSH DPH RETI ; TRANS: MOV A,DBRGTK CJNE A,DBRGPT,TRANS1 ;ANY TRANS TO DO? SETB NOSERO ;NO: SET NO SERIAL OUTPUT FLAG JMP RETSIO ; TRANS1: MOV R0,A MOV SBUF,@R0 ;LOAD TRANS BUFFER TRAN1: INC R0 MOV DBRGTK,R0 CJNE R0,#LimRgOutB,RETSIO ;AT END OF BUFFER? MOV DBRGTK,#RgOutB ;YES: SET TO BEGINNING OF BUFFER RETSIO: POP ACC POP PSW RETI ; ; ; ; CALL ROUTINES FROM INTERRUPTS ; ; ; THIS ROUTINE USED TO TRANSMIT PT DEV IDCODE ; FROM PORT B TO PORT B ; UPON RECEIPT OF 'ALLOW PASS-THRU' CMD. ; SIDSNB: JNB DOTWO,BSND2 ;CONTINUE IF NOT INTERRUPTING CONTINUITY ;OF KBD DATA. SETB DINBB ;FORCE RETURN TO THIS CODE WHEN KBD FINISHES RET ; ; BSND2: PUSH ACC JB INPASS,BSND1 ;JMP IF ALREADY IN 'SET INBOUND PASS' MODE SETB INPASS ;NOT IN IT, SO SET IT MOV A,#SINPAS JNB LOST,BSND3 CLR LOST INC A BSND3: CALL RPUTB ;SEND 'SET IN PASS MODE' BYTE BSND1: MOV A,#IDSYNC CALL RPUTB ;SEND 1 IDSYNC BYTE MOV A,SESCR1 ;SEND 1ST OF 2 IDCODE BYTES CALL RPUTB MOV A,SESCR2 ;SEND 2ND OF 2 IDCODE BYTES CALL RPUTB CLR A MOV SESCR2,A SETB PSTHRU ;SET PASS-THRU FLAG POP ACC RET ; ; THIS ROUTINE IS USED FOR PT. DEV. TO WS ; (PORT B TO PORT A) TRANSFERS ; SCALPA: JNB DOTWO,SESND2 ;CONTINUE IF NOT INTERRUPTING CONTINUITY OF ; KBD DATA. SETB DINBA ;FORCE RETURN TO THIS CODE WHEN KBD FINISHES RET ; ; SESND2: PUSH ACC JB INPASS,SEPAB1 ;JUMP IF ALREADY IN 'SET INBOUND PASS-THRU' MODE SETB INPASS ;NOT IN IT, SO SET IT MOV A,#SINPAS JNB LOST,SEPAB5 CLR LOST INC A SEPAB5: CALL RPUTA JB FULBUF,ENDPBA SEPAB1: MOV A,SESCR1 ;GET BYTE TO OUTPUT CALL RPUTA ;PUT IT IN OUTPUT BUFFER MOV A,SESCR2 ;CHECK FOR 2ND TRANSMISSION CJNE A,#00H,SECAL1 ENDPBA: POP ACC ;RESTORE ACC RET ;RETURN TO BACKGROUND ROUTINE SECAL1: CALL RPUTA CLR A MOV SESCR2,A POP ACC RET ; ; THIS ROUTINE IS USED FOR WS TO PT. DEV. ; (PORT B TO PORT A) TRANSFERS. ; THERE IS NO NEED FOR ANY 'PASS-THRU' BYTES. ; THERE WILL ALWAYS BE ONLY ONE BYTE TRANSMITTED AT A TIME. ; SCALPB: PUSH ACC MOV A,SESCR1 ;GET BYTE TO OUTPUT CALL RPUTA ;PUT IT IN OUTPUT BUFFER POP ACC ;RESTORE ACC RET ;RETURN TO BACKGROUND ROUTINE ; ORG 0F00H ;CurAdd to Keyboard Code translation table ; ; C O L U M N A D D R E S S ; 0 1 2 3 4 5 6 7 8 9 ; DB 3FH,3FH,3FH,3FH,44H,43H,3FH,3FH ;; DB 12H,00H,7FH,04H,05H,0CH,14H,07H ;; 0 ;; DB 3FH,3FH,3FH,3FH,48H,45H,3FH,3FH ;; R DB 31H,09H,71H,61H,13H,11H,4CH,5EH ;; 1 ;; O DB 3FH,3FH,3FH,3FH,6EH,6BH,3FH,3FH ;; DB 39H,69H,6FH,6CH,1DH,1CH,6DH,38H ;; 2 W ;; DB 3FH,3FH,3FH,3FH,2CH,3BH,3FH,3FH ;; DB 2DH,70H,06H,27H,1FH,1EH,2EH,30H ;; 3 ;; A DB 3FH,3FH,3FH,3FH,2FH,0AH,3FH,3FH ;; DB 08H,5BH,5DH,3FH,0EH,02H,4DH,2BH ;; 4 D ;; DB 3FH,3FH,3FH,3FH,49H,46H,3FH,3FH ;; D DB 7CH,7BH,7DH,60H,0BH,01H,4AH,5CH ;; 5 ;; R DB 3FH,3FH,3FH,3FH,4BH,3FH,3FH,3FH ;; DB 1BH,41H,42H,47H,0FH,03H,0DH,21H ;; 6 E ;; DB 3FH,3FH,3FH,3FH,62H,68H,3FH,3FH ;; S DB 37H,79H,75H,6AH,1AH,19H,20H,36H ;; 7 ;; S DB 3FH,3FH,3FH,3FH,63H,66H,3FH,3FH ;; DB 35H,72H,74H,67H,18H,17H,76H,34H ;; 8 ;; DB 3FH,3FH,3FH,3FH,7AH,73H,3FH,3FH ;; DB 33H,77H,65H,64H,16H,15H,78H,32H ;; 9 ORG 0FFFH DB 000H ;PUT CONSTANT HERE TO MAKE CHECKSUM=F0H END