 subtitle  "Serial Serial 9600 bps Interrupt Rx/Tx."
;
;  These Routines are used to provide a Serial (Digital Logic)
;   from a PIC.  These routines do not use the Serial communications
;   hardware in some PICs and does require interrupts.  Therefore,
;   The 16C61, 16C71, and 16C84 are the recommended target devices.
;
;  Note that the input is DIGITAL LOGIC.  For RS-232 "Cheats", the
;   values Read/Output will have to be inverted.
;
;  Currently they are setup for a PIC running at
;   3.579545 MHz (Colour Burst Frequency) and 9600 bps.  This 
;   can be changed by changing the count value.
;
;  Note that the Received Data is put into the 5 byte circular
;   "Buffer".  
;
;  It is assumed that the host the PIC running this code can use
;   a simple 3 wire interface.
;
;  Hardware Notes:
;   The Pic is Running with a 3.579545 MHz (Colour Burst) Crystal for 16C84
;   RA1 is Connected to RS-232 "TX" Line
;   RB0 is Connected to RS-232 "RX" Line 
;  NOTE:  RA1 is used to Transmit data rather than RA0 because
;   RA0 is usually used to look like the LSB of PORTB.
;
;  Myke Predko
;  96.09.22


;  Declarations
 cblock	_VarEnd
BitCount			;  Count Value for the Bits
Char				;  Temporary Storage of the Character Read In
Temp				;  Temporary Value for Program
RRCount				;  Loop Count for Reading the Characters
SCCount
Next				;  Pointer to the Next Value in the Chain
ShadowFSR			;  Saved the FSR Buffer Value 
_w				;  Context Save Registers
_status
BufferStart			;  Character Buffer
Buffer1
Buffer2
Buffer3
BufferEnd
 endc


;  Constants
Bit     EQU     27		;  Delay between the Bits, using the 
BitStart EQU	Bit / 3		;  Delay to check in character middle

;  The Bit Count is Calculated by the Formula:
;
;   Period = 1 / bps            ;  Get the Period required.
;   Total Cycles = ( Period * ( PIC_Frequency / 4 )) - 15
;				;  Number of Cycles to Execute
;   Bit = ( Total_Cycles / 3 ) + 1	
;				;  The actual number of Dlay Loops
;
;  For Example: Getting 2400 bps at 4 MHz:
;   Period = 1 / 2400 = 0.0004167
;   Total Cycles = ( 4.167(10^-4) * 4(10^6) / 4 ) - 15 = 402
;   Bit = ( 402 / 3 ) + 1 = 135


;  RX Interrupt Handler
;  Read Serial Routine - When Character Read and Put in Buffer return to Loop
RXInt

  movwf  _w                     ;  Save the Context Registers
  movf   STATUS, w
  movwf  _status
  bcf    STATUS, RP0            ;  Make Sure we're in Bank 1
 
ReadRS232                       ;  Start of Original Serial Read Routine

  decfsz BitCount		;  Wait for Halfway through the Character
   goto	 $ - 1

  btfss	 PORTB, 0		;  Do we *Still* have the Start Bit?
   goto	 ShortRRLoop		;  Point of Inversion depending on Logic

  movlw  0x010                  ;  Reset Interrupts Before Returning
  movwf  INTCON
  goto   RREnd                  ;  Wait for the Next Transition

ShortRRLoop			;  Now, Loop Around Here for Each Character

  movlw	 Bit + 2		;  Load and wait for the Bit Count
  movwf  BitCount		;   NOTE:  3 goto $ + 1 removed
  decfsz BitCount               ;  Now Dlay
   goto  $ - 1

  rrf    PORTB, w               ;  Get the Bit Value Coming in
  rrf    Char                   ;  Update the Character

  decfsz RRCount                ;  Do 8x
   goto  ShortRRLoop

  movlw	 Bit			;  Now, Look at the Stop Bit
  movwf  BitCount
  decfsz BitCount
   goto  $ - 1

  movlw  0x010                  ;  Reset the Interrupt Enable (In Case
  movwf  INTCON                 ;   one comes in during code below)

  btfss	 PORTB, 0		;  Now, Check to see if the Stop Bit is There
   goto  RREnd			;  It's not, Ignore the Character 

HaveRS232			;  We Have Correct RS232 Value - End and Return

  incf   Next			;  Now, Store the Character in the Buffer

  movlw	 BufferEnd + 1		;  Are we at the End of the Buffer?
  subwf	 Next, w
  btfss	 STATUS, C		;  If Carry Set, No
   movlw BufferStart - ( BufferEnd + 1 )  ;  Else, have to Roll to Start
  addlw	 BufferEnd + 1		;  Reset the Start of Next

  xorwf	 FSR, w			;  Swap Next with FSR (Shadow Contains Current FSR)
  xorwf	 FSR
  xorwf	 FSR, w
  movwf	 Next			;  Save the Old FSR Value

  movf   Char, w                ;  Store the Character in the Buffer
  movwf  INDF

  movf   Next, w		;  Restore the FSR
  xorwf	 FSR, w
  xorwf	 FSR
  xorwf	 FSR, w
  movwf  Next			;  Store the Current Next Value

RREnd                           ;  Return to where the Routine was Called

  movlw  8                      ;  Reload the Bit Counter for the Loop
  movwf  RRCount

  movlw	 BitStart		;  Reload the Counters for the Next Serial Character
  movwf  BitCount

IntEnd				;  Finished with the Interrupt

  movf   _status, w             ;  Restore the Context Registers and Return
  movwf  STATUS
  swapf  _w
  swapf  _w, w

  retfie


;  Interrupt Setup Routine - Init the Variables for the RXInt Handler
;  NOTE:  It is assumed that PORTA.1 is set for Output
RXSetup

  movlw	 BitStart		;  Setup the Values for the Interrupt
  movwf	 BitCount
  movlw	 8			;  Want to Read 8 Characters
  movwf	 RRCount

  movlw	 BufferStart		;  Initialize the Indexes to the Buffer
  movwf	 ShadowFSR
  movwf	 Next

  movlw	 0x090			;  Turn on the Receive Interrupts
  movwf	 INTCON

  return


;  Character Read Routine.  Wait for Something to be Available
GetCHAR

  movf	 ShadowFSR, w		;  Get the Last Character Read 
  subwf	 Next, w		;   in the Buffer and If it 
  btfss	 STATUS, Z		;   hasn't been read - return it
   goto	 GetCHAR		;  Else, Wait for the Next

  incf	 ShadowFSR, w		;  Point to the Next Value in 	
  addlw	 0 - ( BufferEnd + 1 )	;   the Table
  btfsc	 STATUS, C
   addlw BufferStart - ( BufferEnd + 1 )
  addlw	 BufferEnd + 1
  movwf	 ShadowFSR		;  Save it for the Next Read
  
  movwf	 FSR			;  Get the newly entered Character
  movf	 INDF, w

  return


;  Character Send Routine - Send the Character in "w"
SendCHAR			;  Serial Send the Character to the Host

  movwf	 Temp			;  Save the Character to Send

  movlw	 8			;  Setup the 8 Bits to Send
  movwf	 SCCount

  bcf	 PORTA, 1		;  Output the Start Bit

SCLoop				;  Loop Here for Each Character

  movlw	 Bit			;  Delay the Length of the Bit
  movwf	 Count
  decfsz Count
   goto	 $ - 1

  rrf	 Temp			;  Rotate the Bit Over
  btfsc	 STATUS, C		;  Do we have to Send a '1'?
   goto	 SCOne	

  nop				;  Send a Zero
  bcf	 PORTA, 1		;  NOTE: it is Inverted!

  goto	 SCLoopEnd

SCOne				;  Send a One

  bsf	 PORTA, 1		;  NOTE: it is Inverted!

  goto	 $ + 1			;  Delay 2 cycles to Even Up

SCLoopEnd			;  Do this for 8 Bits

  nop				;  To Time Everything Out

  decfsz SCCount
   goto	 SCLoop

  nop				;  Keep everything on track

  movlw	 Bit			;  Delay for the Bit
  movwf	 Count
  decfsz Count
   goto	 $ - 1

  goto	 $ + 1			;  Delay the Correct Number of Cycles
  goto	 $ + 1

  bsf	 PORTA, 1		;  Send a Zero Stop Bit

  nop
  movlw	 Bit			;  Wait the Correct Number of Cycles before Returning
  movwf	 Count
  decfsz Count
   goto	 $ - 1

  return

 PAGE				
