; File: Buffer.A51 Date: 09/23/92 ; ; /* (c) Copyright BLUE EARTH RESEARCH, MANKATO, MN 1991, 1992 */ ; /* All rights reserved. */ ; $ ERRORPRINT PAGELENGTH (60) PAGEWIDTH (110) ; Assembler controls $ XREF TITLE (Serial Input Buffer Routine) $ DATE (09/23/92) NOGEN ; ----------------------------------------------------------------------------- ; 8051 SERIAL INPUT BUFFER ROUTINE ; Written by Tom Hiniker, Blue Earth Research, Mankato, MN 56001 ; ----------------------------------------------------------------------------- ; This program implements a 256 byte circular buffer for receiving serial ; characters via the '51's on-chip UART. The equate table can be changed to ; relocate the buffer and code memory to any user desired memory spaces. ; NOTE: Whenever BASIC writes a character to the on-chip UART, it first tests ; the UART's TI bit to determine if the 1 byte transmit buffer is empty or not. ; If the TI bit is "1", indicating the buffer is now empty, BASIC will then ; clear it and load another character into the buffer. Otherwise, it waits ; until it is a "1". Therefore, if another program should clear the TI bit, ; BASIC will go into a wait forever mode when attempting to write to the serial ; port. ; However, for this serial buffer routine to function properly, the TI bit ; must be cleared or the CPU will continuously service the serial interrupt. ; This will make any other program run very slowly since the CPU will only be ; able to execute one instruction between serial interrupts. This program ; therefore clears the TI bit as part the initialization process. To prevent ; BASIC from hanging up, "SBUF" must be loaded with a character by an assembly ; language routine before any BASIC output routine is used. (TI can only be ; set by the UART and this is the only way to do it.) ; BASIC can write characters to the serial port with the serial interrupt ; enabled, but should call "ClearTI" afterwards to prevent slow program ; operation. ; The serial interrupt should also be disabled when BASIC is expected to ; receive serial characters instead of the serial interrupt routine. This ; should be done before executing an INPUT statement or returning to the ; command mode. $ EJECT ; ----------------------------------------------------------------------------- ; EXAMPLE BASIC LANGUAGE PROGRAM ; ----------------------------------------------------------------------------- ; 10 CALL 7FD5H : REM Initialize serial interrupt ; 20 FOR X=1 TO 4000 : NEXT : REM Wait for some characters ; 30 IF DBY(1FH)=DBY(1EH) THEN 110 : REM Buffer empty ? ; 40 CALL 7FC0H : REM Get buffer character ; 50 IF DBY(1DH)<>36H GOTO 30 : REM Test for "6" ; 60 CALL 7FF8H : REM Write to SBUF ; 70 PRINT "Found a 6 in buffer." ; 80 CALL 7FF0H : REM Clear TI bit ; 90 GOTO 30 : REM Go check buffer again ; 110 CALL 7FF8H : REM Write to SBUF ; 120 PRINT "It works!" ; 130 CALL 7FF0H : REM Clear TI bit ; 200 CALL 7FF6H : REM Disable serial interrupt ; NOTES ON THE SAMPLE BASIC PROGRAM: ; ; Line 10: This line calls the routine that sets up the serial ; port for interrupt driven operation. ; Line 20: This line is for testing purposes only; it simply results ; in a wait loop that allows the buffer to get some characters. ; This line should be taken out for real time execution. ; Line 30: Check if a character has entered the buffer. ; Line 40: Calls the routine that loads the character from the buffer ; to internal memory location 1DH. ; Lines 50-130: These lines demonstrate how an action can be taken based ; on the value of the character in the buffer. Any appropriate ; lines of code can be placed here. ; Line 200: This line needs to be executed only if the BASIC interpreter ; will be used to input serial data via the "INPUT" or "GET" ; statements, or before the BASIC program returns control to ; the user (command mode - the BASIC program will no longer be ; running). ; Also note that whenever BASIC is used to output serial data using the "PRINT" ; statement (as in lines 70 and 120), the "CALL 7FF8H" Statement must be ; executed prior to the PRINT Statement, and the "CALL 7FF0H" Statement must be ; executed immediately after it. $ EJECT ; ----------------------------------------------------------------------------- ; ASSEMBLY LANGUAGE PROGRAM ; ----------------------------------------------------------------------------- ; For each serial character received, the Buffer "Head" address pointer is ; bumped and the received character stored into that location. Reading the ; buffer is accomplished by bumping the Buffer "Tail" address pointer and then ; reading that location into "Char" so BASIC can get it using a simple DBY() ; instruction. If the Head & Tail pointers are equal, then the Buffer is ; considered empty. Trying to read an empty Buffer will produce a NULL code ; (0) in Char and will not advance the Tail pointer. A BASIC program can just ; check for a 0 in Char if it does not want to check Head & Tail pointers ; before doing a "ReadBuffer". ; ----------------------------- EQUATE TABLE ---------------------------------- Buffer EQU 5000H ; Where circular Buffer starts Head EQU 1FH ; Pointer to last character stuffed Tail EQU 1EH ; Pointer to last character read Char EQU 1DH ; Contains retrieved character CodeAddr EQU 7F9FH ; Locate this program at top of ; write protectable memory space CSEG AT CodeAddr ; ---------------------- SERIAL INTERRUPT ROUTINE ----------------------------- SerialInt: JNB RI,NotRecv ; Not a receive interrupt ? PUSH PSW ; Save registers PUSH ACC PUSH DPH PUSH DPL INC Head MOV DPH,#HIGH Buffer ; Page pointer always the same. MOV DPL,Head MOV A,SBUF ; Get serial input character CLR RI ; and reenable interrupt. MOVX @DPTR,A ; Store character in Buffer POP DPL ; Restore registers POP DPH POP ACC POP PSW NotRecv: RETI $ EJECT ; ---------------------- READ CHARACTER FROM BUFFER --------------------------- ReadBuffer: MOV A,Head ; If Head & Tail are the same, XRL A,Tail ; the Buffer is considered empty. JNZ GetChar MOV Char,A ; So return with NULL character RET GetChar: INC Tail MOV DPH,#HIGH Buffer MOV DPL,Tail MOVX A,@DPTR ; Read the Buffer MOV Char,A ; and put it here. RET ; ------------------------INITIALIZE SERIAL INTERRUPT ------------------------- StartSerial: MOV DPTR,#4023H ; Write LJMP code to serial MOV A,#2 ; interrupt vector location. MOVX @DPTR,A INC DPTR MOV A,#HIGH CodeAddr MOVX @DPTR,A INC DPTR MOV A,#LOW CodeAddr MOVX @DPTR,A ; Now initialize Head and Tail pointers so that the first byte received is ; stored in (and read from) location 0 (the pointers are incremented BEFORE ; the byte is stored or read). TMB 9/23/92 MOV Head,#0FFH ; Initialize Buffer pointers MOV Tail,#0FFH MOV Char,#0 ; Initialize Char to "NULL" SETB ES ; Enable serial interrupt SETB EA ; Global interrupts enable ClearTI: JNB TI,$ ; Normally set by BASIC. CLR TI ; We want it cleared. RET $ EJECT ; ------------------------- DISABLE SERIAL INTERRUPT -------------------------- ; May want to load SBUF with 1st character of an output string instead of a ; NULL and let BASIC take care of sending the rest of them. StopSerial: CLR ES ; Disable serial interrupt WriteCR: MOV SBUF,#0DH ; Write to set TI bit RET Write??: MOV SBUF,Char RET END ; Following is the HEX file derived from the assembled source ; file. This can be used for directly loading the program via ; MONITOR-51 and almost any terminal program. :107F9F0030981DC0D0C0E0C083C082051F758350CC :107FAF00851F82E599C298F0D082D083D0E0D0D0DF :107FBF0032E51F651E7003F51D22051E7583508562 :107FCF001E82E0F51D229040237402F0A3747FF00F :107FDF00A3749FF0751FFF751EFF751D00D2ACD2E5 :107FEF00AF3099FDC29922C2AC75990D22851D99AA :017FFF00225F :00000001FF