Ken's computer
D:\PROJECTS\TIMEMON\SERMUX\SERMUX_M.ASM
;-----------------------------------------------------------------------------------------------------
; Serial port multiplexer
; Main processor
;
; This is the PIC16C74 which is connected to the SRAM as well as all the other (slave) processors.
; It is responsible for reading and writing from/to the other processors, buffering data in the
; SRAM, and packaging and unpackaging data to/from routing packets.
;
; Code developed 04/22/98 through 08/09/98
;
;
;****************************************************************************************************
; Overview:
;****************************************************************************************************
;
; The serial port multipler board is used to allow one RS-232 port on a PC to be shared among 7
; devices requiring simple (no handshake) serial I/O. Any of these 7 devices may themselves be
; another (cascaded) multiplexer board.
;
;****************************************************************************************************
; Packets:
;****************************************************************************************************
;
; All data sent to or from the PC is wrapped in packets which facilitate routing and data integrity
; verification. Packets contain the following components:
;
; 1). Prefix
; The prefix provides a means by which the receive logic can be easily synchronized
; with the packet transmission phase. It also identifies the type of packet.
;
; 2). Header
; The header contains routing and/or sequencing information.
;
; 3). Body
; The body contains the data itself.
;
; 4). Suffix
; The suffix contains checksums used to verify the integrity of the data in the header and body.
;
; Note that some types of packet may lack a header, body, or suffix.
;
;
;
;------------------------------------------------------------------------------------------------------
; Nomenclature for "Data packets": (next topic below this one)
;
; When elements of a packet are listed or when elements of a component of a packet are listed,
; the items occuring closer to the top of the page are sent/received first.
;
; <byte>
; An 8-bit (single character) value
;
; <word>
; A 16-bit (2 character) value with the following components:
; <byte> least-significant byte (bits 0..7 of the word, sent/received first)
; <byte> most-significant byte (bits 8..15 of the word, sent/received last)
;
;
;
;------------------------------------------------------------------------------------------------------
; Data Packets:
;
; Data packets may be sent to or from the PC. They contain data which came from or
; will be sent to one of the slave ports or the control channel (this MCU's command
; interpreter).
;
; Data packets may be streamed out back-to-back and are not acknowledged. If the
; checksum fails, the data packet is simply ignored. Lost data packets are detected by
; checking the serial number of each packet.
;
;
; a). Prefix = @P
;
; The prefix consists of the 'at' character (0x40) followed by an uppercase 'P'.
; All packet prefixes consist of the '@' followed by one or more letters.
;
;
; b). Header:
;
; <byte> address/length
;
; Bits 0..4 specify the length:
; Values 0..31 specify 1..32 bytes
;
; Bits 5..7 specify the address:
; Values 0..6 specify slave ports 0..6 (labeled Aux1..Aux7 on the board)
; The value 7 specifies the control channel (messages sent to/from this MCU)
;
; <word> serial number
;
; The serial number is used to identify each packet. It starts at an arbitrary
; number then increases in value by 1 for each subsequent packet sent over the
; RS232 connection to/from the PC.
;
;
; c). Body
;
; The body contains the data (1 to 32 bytes, as specified in the header).
;
; The data bytes themselves are modified via the following algorithm: (using C syntax)
; output_byte = input_byte ^ (cyclic_checksum & 0xff);
; apply_to_checksums( input_byte );
;
; To restore the data to its original value:
; input_byte = output_byte ^ (cyclic_checksum & 0xff);
; apply_to_checksums( input_byte );
;
; This modification of the data is done to prevent packets being sent to cascaded
; multiplexers from being misinterpreted as packets destined for this multiplexer if the
; receive logic gets out of phase.
;
;
; d). Suffix
;
; <byte> plain checksum of header and pre-modified body
;
; <word> cyclic checksum of header and pre-modified body
; The cyclic checksum algorithm is difficult to explain in words but the code
; speaks for itself (see the code for details).
;
;
; The length of a data packet ranges from 9 to 40 bytes
;
;
;
;****************************************************************************************************
; Data flow:
;****************************************************************************************************
;
; PC --> IRQ --> Rx_pipe --> Rx_input() # --> Receive_Buffer[address] --> Service_slave() * --> slave port
; slave port --> Service_slave() ** --> Transmit_Buffer --> Tx_output() ## --> Tx_pipe --> IRQ --> PC
;
; * = packet extracted
; # = prefix and checksum examined and stripped
; ** = packet encoded
; ## = prefix and checksum generated
;
;
;------------------------------------------------------------------------------------------------------
; Data flow from PC to slave port:
;
;
; 1). PC transmits packet addressed to a slave port
;
; 2). IRQ handler buffers data in Rx_pipe
;
; 3). Rx_input() checks and discards the packet prefix ("@P") and header and moves the contents
; of the packet from Rx_pipe to Receive_Buffer for the addressed slave
; If the checksum fails or Receive_Buffer is full then the packet is discarded
;
; 4). Service_slave() spools the contents of the packet off to the slave port
;
;
;------------------------------------------------------------------------------------------------------
; Data flow from slave port to PC:
;
;
; 1). Service_slave() transfers data from the slave processor to a new packet and puts the packet in Transmit_Buffer
; The packet is encoded (except for "@P" prefix and checksums which are generated later)
; Bandwidth useage is optimized by not beginning a packet transfer from a slave processor
; until a full packet worth of data is available or Transmit_Buffer is close to empty
;
; 2). Tx_output() moves data from Transmit_Buffer to Tx_pipe
; The "@P" prefix and checksums are added by Tx_output()
;
; 3). IRQ handler writes data from Tx_pipe to the PC port
;
; 4). PC receives packet addressed from a slave port
;
;
;
;
;****************************************************************************************************
; Data structures:
;****************************************************************************************************
;
; Rx_pipe and Tx_pipe are simple FIFO buffers in register space with one enqueue pointer and
; one dequeue pointer each.
;
; Transmit_Buffer is a 64k FIFO buffer implemented in external SRAM.
;
; There is one 8k Receive_Buffer for each slave port plus one for the control channel.
; The Receive buffers are FIFOs implemented in external SRAM. Each receive buffer has an
; enqueue pointer and a dequeue pointer. There is one global tentative enqueue pointer which is
; used to receive each packet. It is initialized to the value of the enqueue pointer for the
; buffer to which the packet is addressed and then advanced for each character received. Once an
; entire packet has been received and the checksum verified, the enqueue pointer for the addressed
; buffer is set to the value of the tentative enqueue pointer.
;
;
;
;
;****************************************************************************************************
; Control channel messages:
;****************************************************************************************************
;
; The control channel is mapped to address 7. It is written in the same manner as writing to any
; slave port. Data sent from it arrives in the same manner as data arriving from a slave port.
; Rather than being connected to an external port, however, the control channel is "connected"
; to this MCU's command-interpreter. The messages the command-interpreter understands and the
; responses it sends are detailed below:
;
;
; 1). Read registers
;
; "@R" (prefix, 2 bytes)
;
; <byte> Address/length
; Bits 0..1 specify the length:
; Values 0..3 specify 1..4 bytes
;
; Bits 2..4 are 0s ... the packet will be ignored if they are non-zero
;
; Bits 5..7 specify the address:
; Values 0..6 specify slave MCUs 0..6 (corresponding to Aux1..Aux7 ports on the board)
; The value 7 specifies this MCU
;
; <byte> Register start address
; This specifies the first of <length> registers to read
;
; <byte> plain checksum of Address/length and register start address
;
; <word> cyclic checksum of Address/length and register start address
;
;
; Response:
;
; "@r" (prefix, 2 bytes)
;
; <byte> plain checksum of Address/length and register start address (from command packet)
;
; <word> cyclic checksum of Address/length and register start address (from command packet)
;
; length * <byte> data
;
; <byte> plain checksum of data
;
; <word> cyclic checksum of data
;
;
;
; 2). Write registers
;
; "@W" (prefix, 2 bytes)
;
; <byte> Address/length
; Bits 0..1 specify the length:
; Values 0..3 specify 1..4 bytes
;
; Bits 2..4 are 0s ... the packet will be ignored if they are non-zero
;
; Bits 5..7 specify the address:
; Values 0..6 specify slave MCUs 0..6 (corresponding to Aux1..Aux7 ports on the board)
; The value 7 specifies this MCU
;
; <byte> Register start address
; This specifies the first of <length> registers to write
;
; length * <byte> data
;
; <byte> plain checksum of data and header
;
; <word> cyclic checksum of data and header
;
;
; Response:
;
; "@w" (prefix, 2 bytes)
;
; <byte> plain checksum from command packet
;
; <word> cyclic checksum from command packet
;
;
;
; Note that up to 4 registers are read and written via a holding buffer which is copied to/from
; register space all at once so that 32-bit words can be read or written with no danger of
; having the value modified by the operating program in between bytes read or written.
;
;
; 3). Change baud rate
;
; "@B" (prefix, 2 bytes)
;
; <byte> new TXSTA value XOR'd with 0X6C (causes checksums to fail if this packet is mistaken for "read" packet or vice versa)
;
; <byte> new SPBRG value XOR'd with 0X35
;
; <byte> plain checksum of TXSTA and SPBRG (original non-XOR'd values)
;
; <word> cyclic checksum of TXSTA and SPBRG (original non-XOR'd values)
;
;
; This command causes all buffers to be flushed prior to writing TXSTA and SPBRG. It also
; resets the serial port to clear any error flags.
;
;
; Response:
;
; None -- if the command was properly received then the baud rate will be reset and the
; multiplexer will then respond to commands at the new baud rate.
;
;
;
;
;****************************************************************************************************
; Slave processor bus operation:
;****************************************************************************************************
;
; The master controls the bus. It has 8 bits of bidirectional data connected to RB0..7 of each
; slave processor. It also has the following control lines:
;
;
; Name Brief description Driven by port bit
; -----------------------------------------------------------------------------------
; CID1 Cycle ID bit 1 Master RA3
; CID0 Cycle ID bit 0 Master RA2
; R/W High for read cycle, Low for write cycle Master RA1
; E Enable -- active high strobe Master RA0
; Ack Acknowledge -- active low, common-drain Slave RA4
;
;
; Each slave may either be addressed to respond to normal bus read/write cycles or
; to ignore normal bus cycles. A special bus cycle is used to address a specific
; slave processor to "listen" or "unlisten" to normal bus cycles. Only one slave
; processor at a time should be addressed to "listen".
;
;
; All bus cycles (including the special "listen" / "unlisten" one) fall into one
; of two categories: read cycles or write cycles. Note that "read" and "write"
; are from the master's point-of-view.
;
; Write cycle timing:
; 1). Master sets up CID1, CID0, and R/W
; 2). Master waits if Ack is being driven low (prior cycle not yet ended by slave)
; 3). Master drives data onto RB0..7
; 4). Master asserts E
; 5). The addressed slave samples RB0..7
; 6). The addressed slave drives Ack low
; 7). The master de-asserts E (and is then free to change any other signal)
; 8). The addressed slave stops driving Ack low
;
; Read cycle timing:
; 1). Master sets up CID1, CID0, and R/W
; 2). Master waits if Ack is being driven low (prior cycle not yet ended by slave)
; 3). Master asserts E
; 4). The addressed slave drives data onto RB0..7
; 5). The addressed slave drives Ack low
; 6). The master samples RB0..7
; 7). The master de-asserts E (and is then free to change any other signal)
; 8). The addressed slave stops driving RB0..7 and Ack
;
;
; Types of bus cycles:
;
; CID1 CID0 R/W Cycle name Description
; -------------------------------------------------------------------------------------------------
; 0 0 0 Write Tx Data is written to the Transmit Pipe
; 0 0 1 Read Rx Data is read from the Receive Pipe
; 0 1 0 Write IAddr Data is written to the indirect addressing register (IAddr)
; 0 1 1 Poll Tx Reads the number of free bytes in the Transmit Pipe
; 1 0 0 Chip Select Special cycle which addresses a slave to "listen" or "unlisten"
; 1 0 1 Poll Rx Reads the number of bytes waiting in the Receive Pipe
; 1 1 0 Write Indirect Writes the register pointed to by the IAddr register
; 1 1 1 Read Indirect Reads the register pointed to by the IAddr register
;
;
; The Chip Select cycle requires further description:
; Data bits 0..3 contain the address of the slave which should respond to this cycle (and assert Ack)
; Data bit 7 is:
; 1 if the addressed slave should "listen" (respond) to all other types of bus cycle
; 0 if the addressed slave should "unlisten" to (ignore) all other types of bus cycle
; Data bits 4..6 are unused
;
; If a slave is addressed to "unlisten" when it is already in the "unlistened" state then it will
; reset all internal registers (thus disabling the serial port receiver and flushing all buffers).
; This special double unlisten condition is sent by the master as part of its BREAK reset ...
; thus a BREAK sent to the master will reset the entire board to its power-up state.
;
;
; The Write IAddr cycle has special meaning for the following values:
; 0X00 -- Transfers a block of up to 4 registers into the word buffer (read transfer)
; 0X80 -- Transfers a block of up to 4 registers from the word buffer (write transfer)
;
; Prior to executing either of the above commands, the word buffer address and length registers
; should be written in the normal manner. The word buffer itself should be written prior to
; executing a write transfer. It is read to fetch the results of a read transfer.
;
; The data in the word buffer is right-justified (WordBuf3 is always filled, WordBuf2 is
; filled if 2 or more bytes are transfered, etc.)
;
; These special commands facilitate "snapshot" transfers of 32-bit words in order to avoid
; having the value modified by the operating program partway through a read or write.
;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;####################################################################################################
;****************************************************************************************************
; Definitions, macros, variables, constants
;****************************************************************************************************
;####################################################################################################
;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
LIST P=PIC16C74A, R=DEC ; PIC16C74A, radix = base 10
INCLUDE <P16C74A.INC>
;-----------------------------------------------------------------------------
;Software registers (RAM) in bank0
wtemp EQU 0x20 ;EQU 0XA0 -- both banks -- storage for w register for interrupt context save
CBLOCK 0X21
stemp ;for saving status register in IRQ handler
PCLATHTEMP ;for saving PCLATH in IRQ handler
FSRTEMP ;for saving FSR in IRQ handler
Flags1 ;miscellaneous flags -- see individual bit definitions below
Flags2
SlaveFlags ;bits 0..6 correspond to slaves at addresses 0..6 -- if a bit is on then the slave MCU is installed
SlaveAddress ;address of slave being serviced on each iteration of the "service slaves" algorithm
SlaveMask ;bitmask into SlaveFlags byte of slave being serviced on each iteration of the "service slaves" algorithm
SoftTimer1 ;countdown timer which is decremented every 100ms by the timer interrupt until it reaches zero
temp ;temporary register
temp2 ; note that these registers are generally reserved for the highest-level code (main loop and rx, tx state machines)
temp3
temp4
temp5
temp6
mem_temp ;temporary register for use by memory-interface subroutines
mem_temp2
TxBufPCount0 ;counter for number of packets in the transmit buffer
TxBufPCount1 ;high byte of word
TxSerial0 ;serial number to use for next transmit packet encoded
TxSerial1 ;high byte of word
RxSerial0 ;serial number of last packet received
RxSerial1 ;high byte of word
TxBufVCount0 ;counter for number of packets lost due to transmit buffer overflow
TxBufVCount1 ;2nd byte of long word
TxBufVCount2
TxBufVCount3
RxBufMissCount0 ;counter for number of packets detected missing on receive
RxBufMissCount1
RxBufMissCount2
RxBufMissCount3
rx_state ;state of serial port receive logic -- index into jump table
rx_substate ;substate ... used by some state handlers
rx_substate2
rx_substate3
rx_substate4
r232 ;char received from serial port
r232xs ;simple checksum for receive
r232cl ;cyclic checksum low byte for receive
r232ch ;cyclic checksum high byte for receive
tx_state ;state of serial port transmit logic -- index into jump table
tx_substate ;substate ... used by some state handlers
tx_substate2
t232 ;char to transmit to serial port
t232xs ;simple checksum for transmit
t232cl ;cyclic checksum low byte for transmit
t232ch ;cyclic checksum high byte for transmit
crx_state ;state of command interpreter receive logic -- index into jump table
rCmd ;char to process from channel receive buffer
rCmdxs ;simple checksum for rCmd
rCmdcl ;cyclic checksum low byte for rCmd
rCmdch ;cyclic checksum high byte for rCmd
ctx_state ;state of command interpreter transmit logic -- index into jump table
ctx_substate ;substate ... used by some state handlers
tCmd ;char to transmit via command channel
tCmdxs ;simple checksum for tCmd
tCmdcl ;cyclic checksum low byte for tCmd
tCmdch ;cyclic checksum high byte for tCmd
CTxPipeRptr ;Read pointer for command-interpreter transmit pipe
CTxPipeWptr ;Write pointer for command-interpreter transmit pipe
TentativeCTxWptr ;tentative CTxPipe write pointer
CTxPipeCount ;number of bytes in CTx pipe
tmr_softper ;software timer period register for matching 12.8ms interrupt with 100ms timer event
led_code ;LED error code -- 0 = off, 1 = 1PPS, 2 = blink code 2, 3 = blink code 3, etc.
led_codephs ;LED blink code phase
led_bphs ;LED blink phase
ferr_count ;Number of framing errors detected since reset (maxes out at 255)
rc_break_counter ;counter for detecting BREAK condition on Rx line
;Read and write pointers for Tx and Rx pipes (FIFO buffers between IRQ and foreground processes for serial port)
;Note: "transmit" and "receive" are relative to this processor's serial port which connects to the PC,
;thus, all "transmit" buffers hold data which will be sent to the PC and all "receive" buffers hold
;data which came from the PC.
TxPipeRptr ;Read pointer for Transmit pipe
TxPipeWptr ;Write pointer for Transmit pipe
TentativeTxWptr ;tentative TxPipe write pointer
RxPipeRptr ;Read pointer for Receive pipe
RxPipeWptr ;Write pointer for Receive pipe
;Read and write pointers for SRAM queues (FIFO buffers)
;
;The transmit buffer holds all the data received from all the slaves pending transmission
;to the PC. It contains packets lacking the prefix and suffix (checksums).
;
;Each individual receive buffer holds data received from the PC which is waiting to be
;sent to the slave processor it was addressed to.
;
;Receive buffer size = 8k
;Transmit buffer size = 64k
;Transmit_Buffer pointers:
Txwl ;low byte of write pointer for Transmit_Buffer (Enqueue pointer)
Txwh ;high byte of write pointer for Transmit_Buffer
Txrl ;low byte of read pointer for Transmit_Buffer (Dequeue pointer)
Txrh ;high byte of read pointer for Transmit_Buffer
RxBufEmptyFlags ;bits 0..7 correspond to the Receive_Buffer for packet addresses 0..7 -- bit is set if buffer is empty
RxBufIndex ;packet address * 2 of receive buffer read by ReadRxBuf
RxBufMask ;mask for RxBufEmptyFlags bit corresponding to RxBufIndex
RxTwl ;low byte of tentative write pointer for an Rxbuf
RxTwh ;high byte of tentative write pointer for an Rxbuf
CTxPipe ;command-interpreter transmit pipe (runs up through end of bank0 ... should be at least 33 bytes long for optimum efficiency)
ENDC
CTxPipeEnd EQU 0X80 ;first byte past the end of CTxPipe
CBLOCK 0XA1
;Receive_Buffer pointers:
;Note that pointers are grouped so that each pointer belongs to an array of 8 pointers
;which can be indexed by the packet address (* 2)
Rx0rl ;low byte of read pointer for Rx0buf
Rx0rh ;high byte of read pointer for Rx0buf
Rx1rl ;low byte of read pointer for Rx1buf
Rx1rh
Rx2rl
Rx2rh
Rx3rl
Rx3rh
Rx4rl
Rx4rh
Rx5rl
Rx5rh
Rx6rl
Rx6rh
RxMrl ;read pointer for buffer for control channel
RxMrh
Rx0wl ;low byte of write pointer for Rx0buf
Rx0wh ;high byte of write pointer for Rx0buf
Rx1wh
Rx1wl
Rx2wl
Rx2wh
Rx3wl
Rx3wh
Rx4wl
Rx4wh
Rx5wl
Rx5wh
Rx6wl
Rx6wh
RxMwl
RxMwh
;Registers used to transfer data between master or slave MCU registers and the host computer
;via command packets and responses sent to/from the control channel
RegXferMCUAddr ;MCU address of register block to transfer (0..6 are slaves 0..6, 7 is this MCU)
RegXferAddr ;base address of register block to transfer
RegXferLen ;length of register block to transfer (1..4)
RegXfer0 ;low byte of block to transfer
RegXfer1
RegXfer2
RegXfer3 ;high byte of block to transfer
;Note that these registers are used by both the command recieve logic and the command transmit logic --
;receipt and transmission of command messages may not be overlapped... the host PC must wait for a reply
;to arrive for each command it sends or wait until a reasonable timeout elapses before sending another
;command.
ENDC
;------------------------------------------------------------------------------
; Flags contained in the Flags1 register:
POST EQU 0 ;set durring Power-On Self Test -- disables blink-code driver so error LED stays lit
RxPipeEmpty EQU 1 ;receive pipe is empty
TxPipeFull EQU 2 ;transmit pipe is full
Receiving EQU 3 ;serial port is receiving data -- used by interrupt routine to update Rx LED
Transmitting EQU 4 ;serial port is sending data -- used by interrupt routine to update Tx LED
TxBufOverflow EQU 5 ;transmit buffer overflowed -- signal from WriteTxBuf subroutine to its caller
SlaveSelected EQU 6 ;set if the slave at SlaveAddress is addressed to "listen"
CTxPipeFull EQU 7 ;command-interpreter channel transmit pipe is full
;------------------------------------------------------------------------------
; Flags contained in the Flags2 register:
CTxPipeEmpty EQU 0 ;Control channel Tx pipe is empty
RC_FERR EQU 1 ;Framing error detected on last char received
RC_BREAK EQU 2 ;BREAK condition detected on Rx line -- reset baud rate
RC_OERR EQU 3 ;TESTING -- send diagnostic message if overrun detected
;------------------------------------------------------------------------------
;Pipes for buffering data between IRQ and foreground processes
;
;Note: "transmit" and "receive" are relative to this processor's serial port
;which connects to the PC, thus, all "transmit" buffers hold data which will
;be sent to the PC and all "receive" buffers hold data which came from the PC.
;
;The receive pipe ensures that no data is lost when characters come in while
;the foreground process is busy.
;
;The transmit pipe helps keep data streaming out at the maximum rate allowed
;by the serial port.
;
;Since a receive pipe overflow will result in a lost packet whereas a transmit
;pipe underflow only results in a small delay, the receive buffer is larger
;than the transmit buffer.
;
;Receive buffer size = 40 bytes
;Transmit buffer size = 16 bytes
TxPipe EQU 0XC8 ;first byte within the buffer
TxPipeEnd EQU 0XD8 ;first byte past the end of the buffer
RxPipe EQU 0XD8 ;first byte within the buffer
RxPipeEnd EQU 0X00 ;first byte past the end of the buffer (buffer goes up through 0XFF)
;------------------------------------------------------------------------------
;SRAM buffer addresses
;
;The transmit buffer holds all the data received from all the slaves pending transmission
;to the PC. It contains packets lacking the prefix and suffix (checksums).
;
;Each individual receive buffer holds data received from the PC which is waiting to be
;sent to the slave processor it was addressed to.
;
;Receive buffer size = 8k
;Transmit buffer size = 64k
;
;Tx buffer is at 0x10000 + given address (A16 high)
;Rx buffer is at given address (A16 low)
Rx0buf EQU 0X0000 ;holding buffer for data being streamed to slave 0
Rx1buf EQU 0X2000 ;holding buffer for data being streamed to slave 1
Rx2buf EQU 0X4000 ;etc.
Rx3buf EQU 0X6000
Rx4buf EQU 0X8000
Rx5buf EQU 0XA000
Rx6buf EQU 0XC000
RxMbuf EQU 0XE000 ;data being streamed to master -- control messages
Txbuf EQU 0X0000 ;holding buffer for data pending transmission to PC
;----------------------------------------------------------------------------------
; Special addresses within the slave processors' register space.
; These are register locations which the master must access in order to facilitate
; data transfer requests made by the host computer.
;
CBLOCK 0X20
;Word buffer registers
;These registers are used to facilitate "snapshot" transfers of 16 or 32 bit words so
;that the values can be read or written without danger of the value being modified by
;the operating program partway through a read or write.
WordBufAddr ;address base for word transfer
WordBufLen ;length, 1..4
WordBuf0 ;low databyte of word
WordBuf1 ;note that data is right-justified, i.e. a 16-bit word
WordBuf2 ; occupies WordBuf2 and WordBuf3
WordBuf3 ;high databyte of word
ENDC
;------------------------------------------------------------------------------
;Macros
Move MACRO destination,source
MOVFW source
MOVWF destination
ENDM
Movlf MACRO destination,source
MOVLW source
MOVWF destination
ENDM
;Clear global interrupt enable -- note that an IRQ may be acknowledged
;before the BCF instruction and the IRQ may be executed after the BCF
;instruction and leave gie set upon return (thus the BTFSC instruction
;to make sure this hasn't happened)
ClrI MACRO
LOCAL lpclri
lpclri BCF INTCON,GIE
BTFSC INTCON,GIE
GOTO lpclri
ENDM
;Set global interrupt enable
SetI MACRO
BSF INTCON,GIE
ENDM
Bank0 MACRO
BCF STATUS,RP0
ENDM
Bank1 MACRO
BSF STATUS,RP0
ENDM
LowPage MACRO
BCF PCLATH,3
ENDM
HighPage MACRO
BSF PCLATH,3
ENDM
;-------------------------------------------------
; LEDs
LightStatus MACRO
BSF PORTE,0
ENDM
DouseStatus MACRO
BCF PORTE,0
ENDM
LightError MACRO
BSF PORTA,5
ENDM
DouseError MACRO
BCF PORTA,5
ENDM
LightRx MACRO
BSF PORTC,1
ENDM
DouseRx MACRO
BCF PORTC,1
ENDM
LightTx MACRO
BSF PORTC,2
ENDM
DouseTx MACRO
BCF PORTC,2
ENDM
;---------------------------------
; Blink codes:
; The "error" LED is used as a simple output device for reporting errors.
; When an error occurs, a blink-code is set which describes the error.
; The "error" LED is then flashed by the timer interrupt routine. It blinks
; the number of times specified by the code, pauses, then repeats.
;
; Codes:
ERR_RX_PIPE_OVERRUN EQU 2 ;Rx pipe overflowed
ERR_RXREG_OVERRUN EQU 3 ;Hardware Rx overrun
ERR_NO_SLAVE_PORTS EQU 4 ;No slave ports detected
ERR_MEMORY_TEST EQU 5 ;Memory test failed
ERR_BUS_FAILURE EQU 6 ;Bus timeout on attempt to scan for slaves (Ack is asserted)
Blink MACRO code
MOVLW code
CALL set_blink_code
ENDM
;------------------------------------------------
; Slave processor interface
;
; Write macros are invoked with the databyte to write in the W register.
; Read macros end with the data byte read in the W register.
;
; These macros do not time out. Slave processors should be scanned for with a different
; algorithm which does time out. These macros should be used only to communicate
; with slaves which are present.
;
;
; The macros available are:
;
; BusWriteTx writes W to the transmit pipe (of the addressed slave)
; BusReadRx reads W from the receive pipe
; BusWriteIAddr writes W to the IAddr register
; BusPollTx returns number of bytes free in transmit pipe
; BusChipSelect addresses a slave to listen or unlisten
; BusPollRx returns number of bytes buffered in receive pipe
; BusWriteIndirect writes W to the register pointed to by the IAddr register
; BusReadIndirect reads W from the register pointed to by the IAddr register
;
BusWriteTx MACRO ;Write W to addressed slave's transmit pipe
BusStartWrite ;wait for prior cycle to end then drive W onto data bus
MOVLW 0XE0
ANDWF PORTA,F ;setup cycle ID and R/W bits for a "Write Tx" bus cycle
NOP
BSF PORTA,0 ;assert E to start the cycle
BusWaitAck
BusEndWrite
ENDM
BusReadRx MACRO
BusStartRead ;wait for prior cycle to end
MOVFW PORTA
ANDLW 0XE0
IORLW 0X02
MOVWF PORTA ;setup cycle ID and R/W bits for a "Read Rx" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndRead
ENDM
BusWriteIAddr MACRO
BusStartWrite
MOVFW PORTA
ANDLW 0XE0
IORLW 0X04
MOVWF PORTA ;setup cycle ID and R/W bits for a "Write IAddr" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndWrite
ENDM
BusPollTx MACRO
BusStartRead
MOVFW PORTA
ANDLW 0XE0
IORLW 0X06
MOVWF PORTA ;setup cycle ID and R/W bits for a "Poll Tx" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndRead
ENDM
BusChipSelect MACRO
BusStartWrite
MOVFW PORTA
ANDLW 0XE0
IORLW 0X08
MOVWF PORTA ;setup cycle ID and R/W bits for a "Chip Select" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndWrite
ENDM
BusPollRx MACRO
BusStartRead
MOVFW PORTA
ANDLW 0XE0
IORLW 0X0A
MOVWF PORTA ;setup cycle ID and R/W bits for a "Poll Rx" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndRead
ENDM
BusWriteIndirect MACRO
BusStartWrite
MOVFW PORTA
ANDLW 0XE0
IORLW 0X0C
MOVWF PORTA ;setup cycle ID and R/W bits for a "Write Indirect" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndWrite
ENDM
BusReadIndirect MACRO
BusStartRead
MOVFW PORTA
ANDLW 0XE0
IORLW 0X0E
MOVWF PORTA ;setup cycle ID and R/W bits for a "Read Indirect" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
BusWaitAck
BusEndRead
ENDM
;Sub-macros used by above macros
;These sub-macros perform common subfunctions
BusStartWrite MACRO ;disables interrupts, waits for prior cycle to end, then drives W onto bus
LOCAL bus_wait_no_ack
ClrI
MOVWF PORTB
bus_wait_no_ack BTFSS PORTA,4 ;wait for prior cycle to finish if necessary
GOTO bus_wait_no_ack
MOVLW TRISB
MOVWF FSR ;leaves FSR pointing to TRISB
CLRF INDF ;drive data onto bus
ENDM
BusStartRead MACRO ;disables interrupts and waits for ack to be released
LOCAL bus_wait_no_ack
ClrI
bus_wait_no_ack BTFSS PORTA,4 ;wait for prior cycle to finish if necessary
GOTO bus_wait_no_ack
ENDM
BusWaitAck MACRO ;waits for slave to assert Ack
LOCAL bus_wait_ack
bus_wait_ack BTFSC PORTA,4
GOTO bus_wait_ack
ENDM
BusEndWrite MACRO ;Terminates a bus write cycle
BCF PORTA,0
DECF INDF,F ;assumes FSR was set to TRISB at the start of the bus cycle
SetI
ENDM
BusEndRead MACRO ;Terminates a bus read cycle
BCF PORTA,0
MOVFW PORTB ;reads data bus ... although this happens after E is de-asserted it is ok since interrupts are disabled and the slave cannot respond within 200ns to stop driving the data bus
SetI
ENDM
;-------------------------------------------------
; SRAM interface
SetMemCE1 MACRO
BCF PORTE,1
ENDM
ClrMemCE1 MACRO
BSF PORTE,1
ENDM
SetMemCE2 MACRO
BSF PORTC,5
ENDM
ClrMemCE2 MACRO
BCF PORTC,5
ENDM
SetMemWr MACRO
BCF PORTC,3
ENDM
ClrMemWr MACRO
BSF PORTC,3
ENDM
SetMemOE MACRO
BCF PORTC,0
ENDM
ClrMemOE MACRO
BSF PORTC,0
ENDM
SetMemA16 MACRO
BSF PORTC,4
ENDM
ClrMemA16 MACRO
BCF PORTC,4
ENDM
SetMemAClk MACRO
BSF PORTE,2
ENDM
ClrMemAClk MACRO
BCF PORTE,2
ENDM
;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;####################################################################################################
;****************************************************************************************************
; Start of code
;****************************************************************************************************
;####################################################################################################
;||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
;******************************************************************************
;Reset vector
ORG 0
CLRF PCLATH
GOTO start
;******************************************************************************
;Interrupt vector
;
;Interrupt sources:
; TMR2IF -- Timer2 expired
; RCIF -- Serial port received a character
; TXIF -- Serial port is ready to transmit a character
ORG 4
;Save context
MOVWF wtemp ;Save W and status registers
SWAPF STATUS,W ;gets status into W without affecting flags
Bank0
MOVWF stemp
MOVF PCLATH,W
MOVWF PCLATHTEMP
MOVFW FSR
MOVWF FSRTEMP
;Branch to appropriate handler
;If more than one interrupt is pending then the other(s) will be serviced after the RETFIE instruction is executed
;Interrupt sources are prioritized as follows:
; 1). RCIF
; 2). TMR2IF
; 3). TXIF
LowPage
BTFSC PIR1,RCIF
GOTO receive_char
BTFSS PIR1,TMR2IF
GOTO transmit_char
;--------------------------------
; Begin timer 2 interrupt handler
;
;
;Timer2 interrupt occurs every 12.8ms (78.125 Hz) and causes a timer event to be
;executed every 100ms (on average).
;Update Rx and Tx LEDs
;The Receiving and Transmitting flags are set by the foreground process and cleared here every 12.8ms
BTFSC Flags1,POST
GOTO skip_led_update ;don't do anything with the LEDs if the Power-On Self Test is in progress
DouseRx
BTFSC Flags1,Receiving
LightRx
BCF Flags1,Receiving
DouseTx
BTFSC Flags1,Transmitting
LightTx
BCF Flags1,Transmitting
skip_led_update
;Test for "break" on Rx line
BTFSC PORTC,7
GOTO no_break
DECFSZ rc_break_counter,F
GOTO tmr2int_c1
BSF Flags2,RC_BREAK ;BREAK detected -- trigger foreground routine to reset baud rate
GOTO tmr2int_c1
no_break MOVLW 10
MOVWF rc_break_counter
tmr2int_c1
;Update software timer period register and generate 100ms event
MOVLW 0X10 ;interrupt period = 128/1000 of 100ms = 16/125 of 100ms
ADDWF tmr_softper,F
BTFSS STATUS,C
GOTO ret_tmr_int
MOVLW 0X7D ;0x7d = 125
SUBWF tmr_softper,F
;-----------
;100ms event
;check for hardware overrun error
BTFSS RCSTA,OERR
GOTO no_overrun_err
MOVLW ERR_RXREG_OVERRUN ;set blink code unless a higher code has previously been set
SUBWF led_code,W
BTFSC STATUS,C
GOTO no_overrun_err
MOVLW ERR_RXREG_OVERRUN
MOVWF led_code
no_overrun_err
;update timeout timers
MOVF SoftTimer1,F
BTFSS STATUS,Z
DECF SoftTimer1,F ;decrement countdown timer if not already 0
;Display blink codes on the "error" LED
BTFSC Flags1,POST ;blink-code driver is disabled durring Power-On Self Test
GOTO ret_tmr_int
MOVF led_code,F ;code 0 = no error ... no blinking ... LED doused
BTFSC STATUS,Z
GOTO ret_tmr_dl
DECFSZ led_bphs,F
GOTO ret_tmr_dl
LightError
DECFSZ led_codephs,F
GOTO set_led_bp
MOVFW led_code
MOVWF led_codephs
DECFSZ led_codephs,W
GOTO set_led_bp2 ;code 1 (1PPS)?
MOVLW 10 ;code 1 -- blink once per second
GOTO set_led_bpc
set_led_bp2 MOVLW 20 ;code other than 1 -- delay 2 seconds between code repetitions
GOTO set_led_bpc
set_led_bp MOVLW 5 ;LED blinks every 500ms when flashing a code
set_led_bpc MOVWF led_bphs
GOTO ret_tmr_int
ret_tmr_dl DouseError
ret_tmr_int BCF PIR1,TMR2IF ;clear timer2 interrupt flag
GOTO ret_int
; End timer 2 interrupt handler
;------------------------------
;------------------------------------------------------
; Begin serial port receive character interrupt handler
;
receive_char BTFSS RCSTA,OERR ;check for overrun
GOTO rc_no_oerr
BSF Flags2,RC_OERR
BCF RCSTA,CREN
BSF RCSTA,CREN ;overrun occurred -- reset receive logic
GOTO ret_int
rc_no_oerr BCF Flags1,RxPipeEmpty
BCF Flags2,RC_FERR
BTFSS RCSTA,FERR ;Check for framing errors -- keep track of how many occur
GOTO no_ferr
BSF Flags2,RC_FERR
INCF ferr_count,F
BTFSC STATUS,Z
DECF ferr_count,F ;count maxes out at 255
no_ferr MOVF RxPipeWptr,W ;Move character from RCREG to RxPipe
MOVWF FSR
MOVF RCREG,W
MOVWF INDF
MOVLW 10 ;must have 10 consecutive low Rx samples (on timer interrupt) with no characters received before resetting baud rate
MOVWF rc_break_counter
INCF RxPipeWptr,F ;Increment RxPipe write pointer
MOVFW RxPipeWptr
SUBLW RxPipeEnd
MOVLW RxPipe
BTFSC STATUS,Z
MOVWF RxPipeWptr
MOVFW RxPipeWptr ;Check for pipe overflow
SUBWF RxPipeRptr,W
BTFSS STATUS,Z
GOTO ret_int
DouseStatus ;Overflow has occurred -- turn off "ok" status LED
MOVLW ERR_RX_PIPE_OVERRUN ;set blink code unless a higher code has previously been set
SUBWF led_code,W
BTFSC STATUS,C
GOTO ret_int
MOVLW ERR_RX_PIPE_OVERRUN
MOVWF led_code
GOTO ret_int
; End serial port receive character interrupt handler
;------------------------------------------------------
;-------------------------------------------------------
; Begin serial port transmit character interrupt handler
;
; Move character from TxPipe to TXREG
; If TxPipe is empty then disable this interrupt (clear TXIE)
; TXIE is set when data is written to TxPipe
transmit_char BCF Flags1,TxPipeFull
MOVFW TxPipeRptr
MOVWF FSR
SUBWF TxPipeWptr,W
BTFSC STATUS,Z
GOTO clear_txie ;TxPipe is empty
MOVFW INDF ;TxPipe contains data -- move one character to TXREG
MOVWF TXREG
INCF TxPipeRptr,F ;Increment TxPipe read pointer
MOVFW TxPipeRptr
SUBLW TxPipeEnd
MOVLW TxPipe
BTFSC STATUS,Z
MOVWF TxPipeRptr
;Restore context and return from interrupt
ret_int MOVFW FSRTEMP
MOVWF FSR
MOVF PCLATHTEMP,W
MOVWF PCLATH
SWAPF stemp,W ;gets original status to W without affecting flags
MOVWF STATUS
SWAPF wtemp,F ;swaps halves of wtemp
SWAPF wtemp,W ;gets original W back without affecting flags
RETFIE
clear_txie Bank1
BCF PIE1,TXIE ;TxPipe is empty -- disable the "transmit buffer empty" interrupt
Bank0
GOTO ret_int
; End serial port transmit character interrupt handler
;------------------------------------------------------
;****************************************************************************************************
; Entry point from reset vector ... initializations
;****************************************************************************************************
start
;-----------------------------------------------------------------------------
;Initialize all relevent special function registers to their reset states ...
;this is done incase a watchdog timer reset occurs and some SFRs have values
;which would otherwise impair normal operation
CLRF STATUS ;Clear RP0, RP1 and IRP bits
CLRF PCLATH
CLRF INTCON
CLRF T1CON
CLRF T2CON
CLRF SSPCON
CLRF CCP1CON
CLRF RCSTA
CLRF CCP2CON
CLRF ADCON0
Bank1
MOVLW 0XFF
MOVWF 0X81 ;OPTION register -- symbol not defined in stock header
MOVWF TRISA
MOVWF TRISB
MOVWF TRISC
MOVWF TRISD
MOVLW 0X07
MOVWF TRISE
CLRF PIE1
CLRF PIE2
CLRF SSPSTAT
CLRF TXSTA
CLRF SPBRG
CLRF ADCON1
;-----------------------------------------------------------------------------
;Initialize some software registers (any that interrupt service routine uses)
Bank0
MOVLW (1 << POST) | (1 << RxPipeEmpty)
MOVWF Flags1
MOVLW (1 << CTxPipeEmpty)
MOVWF Flags2
MOVLW 10
MOVWF rc_break_counter
CLRF CTxPipeCount
CLRF ferr_count
CLRF TxBufPCount0
CLRF TxBufPCount1
CLRF TxSerial0
CLRF TxSerial1
CLRF TxBufVCount0
CLRF TxBufVCount1
CLRF TxBufVCount2
CLRF TxBufVCount3
MOVLW 1
MOVWF led_code
MOVWF led_codephs
MOVWF led_bphs
Movlf TxPipeRptr,TxPipe ;Read pointer for Transmit pipe
Movlf TxPipeWptr,TxPipe ;Write pointer for Transmit pipe
Movlf RxPipeRptr,RxPipe ;Read pointer for Receive pipe
Movlf RxPipeWptr,RxPipe ;Write pointer for Receive pipe
Movlf CTxPipeRptr,CTxPipe ;Read pointer for command-interpreter Tx pipe
Movlf CTxPipeWptr,CTxPipe ;Write pointer for command-interpreter Tx pipe
;--------------------------------------------------------------------------
; Initialize special function registers (I/O port states, most peripherals)
CLRF PORTA
MOVLW 0X29 ;0010 1001 -- no memory rd or wr, mem CE2 set, A16 low, tx and rx LEDs doused
MOVWF PORTC
MOVLW 0X06 ;0000 0110 -- no error LED, mem CE1 clear, latch clk high
MOVWF PORTE
MOVLW 0X7F ;enable tmr2, prescale 16, postscale 16
MOVWF T2CON
Bank1
MOVLW 0X10
MOVWF TRISA ;RA4 is the only input (/Ack)
MOVLW 0XFF
MOVWF TRISB ;port B is data bus
MOVLW 0X80
MOVWF TRISC ;port C is all output except RC7/Rx
CLRF TRISD ;port D is the memory address bus -- all output
CLRF TRISE ;port E is all output
Movlf ADCON1,6 ;configure RA0..5 and RE0..2 as digital I/O rather than analog inputs
MOVLW 249 ;tmr2 period 250 x 256 x 4 (prescale, postscale, fixed /4) = 256000 = 12.8ms with 20MHz oscillator
MOVWF PR2
MOVLW (1 << TMR2IE) | (1 << RCIE) ;enable tmr2 interrupt and receive character interrupt (TXIE is set when TxPipe is written to ... cleared when TxPipe becomes empty)
MOVWF PIE1
Bank0
MOVLW (1 << GIE) | (1 << PEIE) ;enable peripheral interrupts (tmr2, serial port)
MOVWF INTCON
;**********************************************************************************************************************
;Begin Power-On Self Test
;*********************************************************************************************************************
LightStatus ;turn on all LEDs durring the POST
NOP
LightError
NOP
LightRx
NOP
LightTx
;------------------------------------------------------------
;Test the SRAM
;First, fill the memory with pseudo-random numbers...
CLRF r232xs ;checksums and checksum algorithm is used to produce pseudo-random numbers for memory test
CLRF r232cl
CLRF r232ch
CLRF t232xs
CLRF t232cl
CLRF t232ch
CLRF Txwl
CLRF Txwh
Movlf FSR,Txwl
ClrMemA16
lprtw1 CLRWDT
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL update_rx_checksums
MOVFW r232xs
XORWF r232cl,W
XORWF r232ch,W
CALL update_tx_checksums
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL WriteMem
INCF Txwl,F
BTFSC STATUS,Z
INCFSZ Txwh,F
GOTO lprtw1
SetMemA16
lprtw2 CLRWDT
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL update_rx_checksums
MOVFW r232xs
XORWF r232cl,W
XORWF r232ch,W
CALL update_tx_checksums
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL WriteMem
INCF Txwl,F
BTFSC STATUS,Z
INCFSZ Txwh,F
GOTO lprtw2
;Now read back and verify the pseudo-random numbers and, at the same time,
;write back the one's compliments of those numbers
DouseTx
NOP
DouseStatus
CLRF r232xs
CLRF r232cl
CLRF r232ch
CLRF t232xs
CLRF t232cl
CLRF t232ch
ClrMemA16
lprtr1 CLRWDT
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL update_rx_checksums
MOVFW r232xs
XORWF r232cl,W
XORWF r232ch,W
CALL update_tx_checksums
CALL ReadMem
MOVWF temp
XORWF t232xs,W
XORWF t232cl,W
XORWF t232ch,W
BTFSS STATUS,Z
GOTO mem_test_failed
MOVFW temp
XORLW 0XFF
CALL WriteMem
INCF Txwl,F
BTFSC STATUS,Z
INCFSZ Txwh,F
GOTO lprtr1
SetMemA16
lprtr2 CLRWDT
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL update_rx_checksums
MOVFW r232xs
XORWF r232cl,W
XORWF r232ch,W
CALL update_tx_checksums
CALL ReadMem
MOVWF temp
XORWF t232xs,W
XORWF t232cl,W
XORWF t232ch,W
BTFSS STATUS,Z
GOTO mem_test_failed
MOVFW temp
XORLW 0XFF
CALL WriteMem
INCF Txwl,F
BTFSC STATUS,Z
INCFSZ Txwh,F
GOTO lprtr2
;Now read back and verify the one's compliments of the pseudo-random numbers
DouseError
CLRF r232xs
CLRF r232cl
CLRF r232ch
CLRF t232xs
CLRF t232cl
CLRF t232ch
ClrMemA16
lprtr3 CLRWDT
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL update_rx_checksums
MOVFW r232xs
XORWF r232cl,W
XORWF r232ch,W
CALL update_tx_checksums
CALL ReadMem
XORWF t232xs,W
XORWF t232cl,W
XORWF t232ch,W
XORLW 0XFF
BTFSS STATUS,Z
GOTO mem_test_failed
INCF Txwl,F
BTFSC STATUS,Z
INCFSZ Txwh,F
GOTO lprtr3
SetMemA16
lprtr4 CLRWDT
MOVFW t232xs
XORWF t232cl,W
XORWF t232ch,W
CALL update_rx_checksums
MOVFW r232xs
XORWF r232cl,W
XORWF r232ch,W
CALL update_tx_checksums
CALL ReadMem
XORWF t232xs,W
XORWF t232cl,W
XORWF t232ch,W
XORLW 0XFF
BTFSS STATUS,Z
GOTO mem_test_failed
INCF Txwl,F
BTFSC STATUS,Z
INCFSZ Txwh,F
GOTO lprtr4
GOTO mem_test_passed
mem_test_failed Blink ERR_MEMORY_TEST
mem_test_passed
;------------------------------------------------------------------------------------
; Scan for slave processors -- the board may have up to 7 slave processors installed
DouseRx
CLRF temp ;slave address
CLRF SlaveAddress
Movlf temp2,1 ;SlaveFlags bitmask for addressed slave
MOVWF SlaveMask
CLRF SlaveFlags
loop_scan_slaves Movlf SoftTimer1,2 ;give each processor 100-200ms to respond (should respond in a few microseconds but timer resolution is 100ms)
MOVFW temp ;loop for each slave 0..6
MOVWF PORTB ;address the slave to "unlisten"
wait_no_ack CLRWDT
BTFSC PORTA,4 ;wait for prior cycle to finish if necessary
GOTO ack_is_off
MOVF SoftTimer1,F ;check for timeout ... this would indicate a serious bus malfunction
BTFSS STATUS,Z
GOTO wait_no_ack
Blink ERR_BUS_FAILURE
GOTO end_slave_scan
ack_is_off MOVLW TRISB
MOVWF FSR ;leaves FSR pointing to TRISB
CLRF INDF ;drive data onto bus
MOVFW PORTA
ANDLW 0XE0
IORLW 0X08
MOVWF PORTA ;setup cycle ID and R/W bits for a "Chip Select" bus cycle
IORLW 0X01
MOVWF PORTA ;assert E to start the cycle
wait_ack CLRWDT
BTFSS PORTA,4 ;wait for acknowledge
GOTO ack_is_on
MOVF SoftTimer1,F ;check for timeout ... this would indicate that the addressed slave is not present
BTFSS STATUS,Z
GOTO wait_ack
GOTO ack_timed_out
ack_is_on MOVFW temp2 ;slave is present -- set corresponding flag bit
IORWF SlaveFlags,F
ack_timed_out BCF PORTA,0 ;de-assert E ... end the write cycle
DECF INDF,F ;tri-state the databus
INCF temp,F ;index/address the next slave
RLF temp2,F
BTFSS temp2,7
GOTO loop_scan_slaves
end_slave_scan MOVF SlaveFlags,F ;are any slave ports present?
MOVLW ERR_NO_SLAVE_PORTS
BTFSC STATUS,Z
CALL set_blink_code
;*********************************************************************************************************************
;End Power-On Self Test
;*********************************************************************************************************************
DouseError
NOP
DouseTx
NOP
DouseRx
LightStatus
MOVLW 2
SUBWF led_code,W
BTFSC STATUS,C
DouseStatus
;---------------------------------------------------------------------------------------------------------------------
; 2nd phase of initializations... (Power-On Self Test has now finished)
;Initialize most software registers (variables)
;First set up default baud rate to 2400 ... this is the power-on value
;The baud rate can be changed later via software commands
;Entry point for BREAK command (break condition on serial port receive line)
;The break command is used to reset the baud rate to 2400
BREAK_RESET Bank1
MOVLW (1 << TXEN) ;enable serial port transmit, not BRGH, asynchronous mode, 8 bit
MOVWF RegXfer0 ;this register is later transfered to TXSTA
MOVLW 129 ;2400 baud
MOVWF RegXfer1 ;this register is later transfered to SPBRG
Bank0
;Entry point for changing baud rate ... flushes all buffers and resets port at new baud rate
;Also resets all slave ports to their power-up (inactive) state
;TXSTA is copied from RegXfer0
;SPBRG is copied from RegXfer1
RESET_BAUD ClrI
MOVLW 1 << SPEN
MOVWF RCSTA ;Disable serial port receiver but leave I/O pins configured as Rx and Tx
;Reset slaves by "unlistening" them while they are already in an "unlistened" state
;This will trigger them to reset to their power-on state (clear all buffers, disable serial port receiver, etc.)
BTFSS Flags1,SlaveSelected ;is a slave addressed?
GOTO no_unl_slave_rst
MOVFW SlaveAddress
BusChipSelect ;tell last slave to "unlisten"
BCF Flags1,SlaveSelected
no_unl_slave_rst MOVF SlaveFlags,F ;are any slaves connected?
BTFSC STATUS,Z
GOTO no_slaves_rst
MOVLW 2
MOVWF temp ;loop counter for assurring that each slave is "unlistened" at least once
next_slave_rst INCF SlaveAddress,F ;compute address of next connected slave
BCF STATUS,C
RLF SlaveMask,F
BTFSS SlaveMask,7
GOTO no_lsm_roll_rst
CLRF SlaveAddress
Movlf SlaveMask,1
DECF temp,F
BTFSC STATUS,Z
GOTO no_slaves_rst ;done "unlistening" all the slaves
no_lsm_roll_rst MOVFW SlaveMask
ANDWF SlaveFlags,W
BTFSC STATUS,Z
GOTO next_slave_rst
MOVF SlaveAddress,W ;"unlisten" next slave
BusChipSelect
GOTO next_slave_rst
no_slaves_rst MOVLW (1 << RxPipeEmpty)
MOVWF Flags1
MOVLW (1 << CTxPipeEmpty)
MOVWF Flags2
MOVLW 10
MOVWF rc_break_counter
CLRF CTxPipeCount
CLRF ferr_count
CLRF TxBufPCount0
CLRF TxBufPCount1
CLRF TxSerial0
CLRF TxSerial1
CLRF TxBufVCount0
CLRF TxBufVCount1
CLRF TxBufVCount2
CLRF TxBufVCount3
Movlf TxPipeRptr,TxPipe ;Read pointer for Transmit pipe
Movlf TxPipeWptr,TxPipe ;Write pointer for Transmit pipe
Movlf RxPipeRptr,RxPipe ;Read pointer for Receive pipe
Movlf RxPipeWptr,RxPipe ;Write pointer for Receive pipe
Movlf CTxPipeRptr,CTxPipe ;Read pointer for command-interpreter Tx pipe
Movlf CTxPipeWptr,CTxPipe ;Write pointer for command-interpreter Tx pipe
SetI
CLRF rx_state ;Rx state starts at 0
CLRF tx_state ;Tx state starts at 0
CLRF ctx_state ;command Tx state
CLRF crx_state ;command Rx state
Movlf Txwl,LOW(Txbuf) ;low byte of write pointer for Transmit_Buffer (Enqueue pointer)
Movlf Txwh,HIGH(Txbuf) ;high byte of write pointer for Transmit_Buffer
Movlf Txrl,LOW(Txbuf) ;low byte of read pointer for Transmit_Buffer (Dequeue pointer)
Movlf Txrh,HIGH(Txbuf) ;high byte of read pointer for Transmit_Buffer
Movlf RxBufEmptyFlags,0XFF ;all buffers are empty
Bank1
Movlf Rx0rl,LOW(Rx0buf) ;low byte of read pointer for Rx0buf
Movlf Rx1rl,LOW(Rx1buf) ;low byte of read pointer for Rx1buf
Movlf Rx2rl,LOW(Rx2buf)
Movlf Rx3rl,LOW(Rx3buf)
Movlf Rx4rl,LOW(Rx4buf)
Movlf Rx5rl,LOW(Rx5buf)
Movlf Rx6rl,LOW(Rx6buf)
Movlf RxMrl,LOW(RxMbuf) ;low byte of read pointer for buffer for control channel
Movlf Rx0rh,HIGH(Rx0buf) ;high byte of read pointer for Rx0buf
Movlf Rx1rh,HIGH(Rx1buf)
Movlf Rx2rh,HIGH(Rx2buf)
Movlf Rx3rh,HIGH(Rx3buf)
Movlf Rx4rh,HIGH(Rx4buf)
Movlf Rx5rh,HIGH(Rx5buf)
Movlf Rx6rh,HIGH(Rx6buf)
Movlf RxMrh,HIGH(RxMbuf)
Movlf Rx0wl,LOW(Rx0buf) ;low byte of write pointer for Rx0buf
Movlf Rx1wl,LOW(Rx1buf)
Movlf Rx2wl,LOW(Rx2buf)
Movlf Rx3wl,LOW(Rx3buf)
Movlf Rx4wl,LOW(Rx4buf)
Movlf Rx5wl,LOW(Rx5buf)
Movlf Rx6wl,LOW(Rx6buf)
Movlf RxMwl,LOW(RxMbuf)
Movlf Rx0wh,HIGH(Rx0buf) ;high byte of write pointer for Rx0buf
Movlf Rx1wh,HIGH(Rx1buf)
Movlf Rx2wh,HIGH(Rx2buf)
Movlf Rx3wh,HIGH(Rx3buf)
Movlf Rx4wh,HIGH(Rx4buf)
Movlf Rx5wh,HIGH(Rx5buf)
Movlf Rx6wh,HIGH(Rx6buf)
Movlf RxMwh,HIGH(RxMbuf)
;Enable and initialize the serial port hardware
MOVF RegXfer0,W
MOVWF TXSTA
MOVF RegXfer1,W
MOVWF SPBRG
Bank0
MOVLW (1 << SPEN) | (1 << CREN) ;enable serial port, enable receiver
MOVWF RCSTA
;#####################################################################################################################
;*********************************************************************************************************************
; Main foreground loop
;*********************************************************************************************************************
mainloop CLRWDT
BTFSS Flags1,RxPipeEmpty
GOTO process_rx
;------ poll_slaves ------
;This code only gets executed once the RxPipe has been emptied
;
;Select next slave and ask it if it has any data ... read the data if so
;Also write data from its corresponding receive buffer if it is ready to accept it
BTFSS Flags1,SlaveSelected ;is a slave addressed?
GOTO no_unlisten_slave
MOVFW SlaveAddress
BusChipSelect ;tell last slave to "unlisten"
BCF Flags1,SlaveSelected
no_unlisten_slave MOVF SlaveFlags,F ;are any slaves connected?
BTFSC STATUS,Z
GOTO no_slaves
next_slave_address INCF SlaveAddress,F ;compute address of next connected slave
BCF STATUS,C
RLF SlaveMask,F
BTFSS SlaveMask,7
GOTO no_lsm_roll
CLRF SlaveAddress
Movlf SlaveMask,1
no_lsm_roll MOVFW SlaveMask
ANDWF SlaveFlags,W
BTFSC STATUS,Z
GOTO next_slave_address
MOVLW 0X80 ;Select next slave
IORWF SlaveAddress,W
BusChipSelect
BSF Flags1,SlaveSelected
BusPollRx ;see if slave has any data waiting
BTFSC STATUS,Z
GOTO no_slave_rxdata
MOVWF temp
SUBLW 31 ;is there at least 32 bytes bufferred?
BTFSC STATUS,C
GOTO check_txbuf_size ;no, don't encode a packet unless the transmit buffer is close to empty
Movlf temp,32 ;encode a packet holding 32 bytes of data
GOTO encode_packet
check_txbuf_size MOVF TxBufPCount1,F ;If the Transmit buffer is close to empty then encode the packet regardless of its size
BTFSS STATUS,Z
GOTO no_slave_rxdata
MOVLW 8 ;less than 8 packets buffered?
SUBWF TxBufPCount0,W
BTFSC STATUS,C
GOTO no_slave_rxdata
encode_packet MOVFW Txwl ;save transmit buffer enqueue pointer incase it overflows
MOVWF temp3 ;(so the addition of this latest packet can be undone)
MOVFW Txwh
MOVWF temp4
SWAPF SlaveAddress,W
MOVWF temp2
RLF temp2,F
MOVLW 0XE0
ANDWF temp2,F ;slave address goes in bits 5..7
DECF temp,W
IORWF temp2,W ;size encoded in packet header is (byte_count - 1) and it goes in bits 0..4
CALL WriteTxBuf
MOVFW TxSerial0
CALL WriteTxBuf
MOVFW TxSerial1
CALL WriteTxBuf
move_packet_loop BusReadRx
CALL WriteTxBuf
DECFSZ temp,F
GOTO move_packet_loop
BTFSS Flags1,TxBufOverflow ;did the Transmit buffer overflow?
GOTO no_txbuf_overflow
BCF Flags1,TxBufOverflow ;consume the flag
MOVFW temp3 ;reset write pointer to its value before this last packet was added
MOVWF Txwl ;(discard the packet)
MOVFW temp4
MOVWF Txwh
INCF TxBufVCount0,F ;increment packet loss counter
BTFSC STATUS,Z
INCF TxBufVCount1,F
BTFSC STATUS,Z
INCF TxBufVCount2,F
BTFSC STATUS,Z
INCF TxBufVCount3,F
GOTO no_slave_rxdata
no_txbuf_overflow INCF TxSerial0,F ;increment transmit packet serial number
BTFSC STATUS,Z
INCF TxSerial1,F
INCF TxBufPCount0,F ;increment transmit buffer packet counter
BTFSC STATUS,Z
INCF TxBufPCount1,F
no_slave_rxdata
;Check if there is data to send to the slave... if so check if slave can accept any ... if so send it
MOVFW SlaveMask
ANDWF RxBufEmptyFlags,W
BTFSS STATUS,Z
GOTO no_slave_txdata
BusPollTx ;see if slave can accept any more transmit data
BTFSC STATUS,Z
GOTO no_slave_txdata
MOVWF temp
Move RxBufIndex,SlaveAddress
ADDWF RxBufIndex,F
Move RxBufMask,SlaveMask
fill_txpipe_loop CALL ReadRxBuf
BusWriteTx
MOVFW RxBufMask
ANDWF RxBufEmptyFlags,W ;loop while there is data to send
BTFSS STATUS,Z
GOTO no_slave_txdata
DECFSZ temp,F ; ... and the slave's Tx Pipe is not full
GOTO fill_txpipe_loop
no_slave_txdata
no_slaves
;Encode a packet from the control channel if enough bytes are buffered or the transmit buffer is empty
BTFSC Flags2,CTxPipeEmpty ;is there any data in the control channel's transmit pipe?
GOTO no_control_packet ; no
BTFSC Flags1,CTxPipeFull ;is control channel's transmit pipe full?
GOTO encode_control_packet ; yes, encode a packet
MOVF ctx_state,F ;is the control channel still generating output?
BTFSS STATUS,Z
GOTO no_control_packet ; yes, wait until it stops or its pipe fills
encode_control_packet MOVFW Txwl ;save transmit buffer enqueue pointer incase it overflows
MOVWF temp3 ;(so the addition of this latest packet can be undone)
MOVFW Txwh
MOVWF temp4
MOVFW CTxPipeCount ;also save CTx pipe position .. don't loose data from the control channel
MOVWF temp5 ;since it can simply wait to encode more output
MOVFW CTxPipeRptr
MOVWF temp6
Move temp,CTxPipeCount
MOVLW 33
SUBWF temp,W
MOVLW 32
BTFSC STATUS,C
MOVWF temp ;temp is packet size .. truncated to 32 bytes if necessary
DECF temp,W ;size-1 goes in low 5 bits
IORLW 0XE0 ;address (=7) goes in high 3 bits
CALL WriteTxBuf
MOVFW TxSerial0
CALL WriteTxBuf
MOVFW TxSerial1
CALL WriteTxBuf
ec_packet_loop Move FSR,CTxPipeRptr
MOVFW INDF
CALL WriteTxBuf
INCF CTxPipeRptr,F
MOVFW CTxPipeRptr
XORLW CTxPipeEnd
MOVLW CTxPipe
BTFSC STATUS,Z
MOVWF CTxPipeRptr
DECF CTxPipeCount,F
DECFSZ temp,F
GOTO ec_packet_loop
BTFSS Flags1,TxBufOverflow ;did the Transmit buffer overflow?
GOTO no_txbuf_overflow2
BCF Flags1,TxBufOverflow ;consume the flag
MOVFW temp3 ;reset write pointer to its value before this last packet was added
MOVWF Txwl ;(discard the packet)
MOVFW temp4
MOVWF Txwh
MOVFW temp5 ;reset CTx pipe position to value before write attempt
MOVWF CTxPipeCount
MOVFW temp6
MOVWF CTxPipeRptr
GOTO no_control_packet ;Note: packet loss counter not incremented since data was left in CTxPipe
no_txbuf_overflow2 BCF Flags1,CTxPipeFull ;update CTx pipe status flags
MOVF CTxPipeCount,F
BTFSC STATUS,Z
BSF Flags2,CTxPipeEmpty
INCF TxSerial0,F ;increment transmit packet serial number
BTFSC STATUS,Z
INCF TxSerial1,F
INCF TxBufPCount0,F ;increment transmit buffer packet counter
BTFSC STATUS,Z
INCF TxBufPCount1,F
no_control_packet
;Drive the Tx state machine through another state if the Tx Pipe isn't full
BTFSC Flags1,TxPipeFull ;can this processor's transmit pipe accept another character?
GOTO tx_return
MOVF tx_state,F ;is any transmit activity currently occurring? (state 0 = idle)
BTFSS STATUS,Z
GOTO process_tx
;Transmit state is currently idle...
;check if Transmit_Buffer contains any packets ... begin transmitting a packet if so
MOVFW TxBufPCount1
IORWF TxBufPCount0,W
BTFSS STATUS,Z
INCF tx_state,F ;tx state 1 begins transmission of next packet in TxBuf
tx_return CLRWDT
;If there is any data waiting in the Receive Buffer for the command-interpreter then process it
BTFSS RxBufEmptyFlags,7
GOTO process_command_rx
;If the command-interpreter is sending anything then drive its tx state machine through another state
;Note that tx states are started by the command_rx state machine ... commands and responses are ping-ponged (not streamed)
BTFSC Flags1,CTxPipeFull
GOTO mainloop
MOVF ctx_state,F
BTFSS STATUS,Z
GOTO process_command_tx
BTFSS Flags2,RC_BREAK ;has a BREAK condition been detected?
GOTO ml_nobreak
CLRF RCSTA ; yes, shut off serial port so its logic resets
BTFSS Flags1,SlaveSelected ;is a slave addressed?
GOTO nus_brk
MOVFW SlaveAddress
BusChipSelect ; yes, tell last slave to "unlisten"
BCF Flags1,SlaveSelected
nus_brk GOTO BREAK_RESET ;Reset baud rate
ml_nobreak GOTO mainloop
;--------------------------------------------------------------------------------------
; Receive 1 character from RxPipe and process it via the Rx state machine
process_rx MOVFW RxPipeRptr
MOVWF FSR
SUBWF RxPipeWptr,W
BTFSC STATUS,Z
GOTO rx_pipe_is_empty
BSF Flags1,Receiving
MOVFW INDF
MOVWF r232
INCF RxPipeRptr,F ;Increment RxPipe read pointer
MOVFW RxPipeRptr
SUBLW RxPipeEnd
MOVLW RxPipe
BTFSC STATUS,Z
MOVWF RxPipeRptr
MOVLW HIGH RxJmpTbl
MOVWF PCLATH
MOVF rx_state,W
ADDLW LOW RxJmpTbl
BTFSC STATUS,C
INCF PCLATH,F
MOVWF PCL ;jump to handler for current rx_state
;State-machine state handler takes over from here ...
;
;The context for the state handlers is as follows:
; 1). The r232 register contains the next character from the input stream
; 2). Control should be returned to rx_done (jump to the label below when done)
; or, alternately, to rx_done_noxs if the checksums need to be preserved
; rather than updated
rx_done MOVFW r232
CALL update_rx_checksums
rx_done_noxs GOTO mainloop
rx_pipe_is_empty BSF Flags1,RxPipeEmpty
GOTO mainloop
;------------------------------------------------------------------------------------------------
; TxPipe is not full (or has not yet been detected as being full) and tx_state is not idle --
; drive the Tx state machine through 1 state/iteration (each state/iteration typically produces
; one output character which is written to the transmit pipe)
process_tx INCF TxPipeWptr,W ;tentatively increment TxPipe write pointer (to detect a full pipe)
MOVWF TentativeTxWptr
SUBLW TxPipeEnd
MOVLW TxPipe
BTFSC STATUS,Z
MOVWF TentativeTxWptr
MOVFW TxPipeRptr ;check to see if pipe is full
SUBWF TentativeTxWptr,W
BTFSC STATUS,Z
GOTO tx_pipe_is_full
BSF Flags1,Transmitting
MOVLW HIGH TxJmpTbl
MOVWF PCLATH
DECF tx_state,W ;tx_state = 1 is the first entry in the table
ADDLW LOW TxJmpTbl
BTFSC STATUS,C
INCF PCLATH,F
MOVWF PCL ;jump to handler for current tx_state
;State-machine state handler takes over from here ...
;
;The context for the state handlers is as follows:
; 1). The character produced should be written to t232 ... it will be written to the TxPipe below
; 2). Control should be returned to tx_done (below) if a character was written, to tx_null
; if a character was not written, or to tx_done_noxs if a character was written but
; should not be applied to the checksums
tx_done MOVFW t232
call update_tx_checksums
tx_done_noxs Move FSR,TxPipeWptr
Move INDF,t232
Move TxPipeWptr,TentativeTxWptr
Bank1
BSF PIE1,TXIE
Bank0
tx_null GOTO tx_return
tx_pipe_is_full BSF Flags1,TxPipeFull
GOTO tx_return
;------------------------------------------------------------------------------------------
; Read 1 character from Receive_Buffer[7] and process it via the command Rx state machine
process_command_rx Movlf RxBufIndex,14
Movlf RxBufMask,0X80
CALL ReadRxBuf
MOVWF rCmd
MOVLW HIGH CRxJmpTbl
MOVWF PCLATH
MOVF crx_state,W
ADDLW LOW CRxJmpTbl
BTFSC STATUS,C
INCF PCLATH,F
MOVWF PCL ;jump to handler for current rx_state
;State-machine state handler takes over from here ...
;
;The context for the state handlers is as follows:
; 1). The rCmd register contains the next character from the input stream
; 2). Control should be returned to crx_done (jump to the label below when done)
; or, alternately, to crx_done_noxs if the checksums should not be updated
crx_done MOVFW rCmd
CALL update_crx_checksums
crx_done_noxs
;------ Testing -- printf( "rxc %.2X s %.2X xs %.2X cl %.2X ch %.2X\n", rCmd, crx_state, rCmdxs, rCmdcl, rCmdch );
; to the auxiliary port at address 6
;
; MOVLW 'r'
; CALL WriteRxBuf6
; MOVLW 'x'
; CALL WriteRxBuf6
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF rCmd,W
; CALL WriteNybRB6
; MOVF rCmd,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 's'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF crx_state,W
; CALL WriteNybRB6
; MOVF crx_state,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'x'
; CALL WriteRxBuf6
; MOVLW 's'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF rCmdxs,W
; CALL WriteNybRB6
; MOVF rCmdxs,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW 'l'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF rCmdcl,W
; CALL WriteNybRB6
; MOVF rCmdcl,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW 'h'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF rCmdch,W
; CALL WriteNybRB6
; MOVF rCmdch,W
; CALL WriteNybRB6
; MOVLW 13
; CALL WriteRxBuf6
; MOVLW 10
; CALL WriteRxBuf6
GOTO mainloop
;------ Testing -- write hexadecimal representation of nybble to channel 6's receive buffer ------
;WriteNybRB6 andlw 0x0f
; addlw 0xf6
; btfsc STATUS,C
; addlw 7
; addlw 58
;
;------ Testing -- write character to channel 6's receive buffer ------
;WriteRxBuf6 MOVWF PORTB
; ClrMemA16
; Bank1
; MOVF Rx6wl,W
; Bank0
; MOVWF PORTD
; ClrMemAClk
; Bank1
; MOVF Rx6wh,W
; Bank0
; SetMemAClk
; MOVWF PORTD
; Movlf FSR,TRISB
; CLRF INDF
; SetMemCE1
; SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
;
; Bank1
; INCF Rx6wl,F ;increment the enqueue pointer
; BTFSS STATUS,Z ;Carry out of low byte?
; GOTO wrxbuf6_noihbp ; no, no need to increment high byte
;
; INCF Rx6wh,F
; MOVLW 0X1F ;check for a carry from bit 4 to bit 5 (high 3 bits are buffer address ... low 5 are offset)
; ANDWF Rx6wh,W
; BTFSS STATUS,Z ;Carry from bit4 to bit5 ?
; GOTO wrxbuf6_noihbp ; no, done
;
; DECF Rx6wh,F ; yes, restore the high 3 bits and clear the low 5 bits
; MOVLW 0XE0
; ANDWF Rx6wh,F
;
;wrxbuf6_noihbp Bank0
; ClrMemWr
; ClrMemCE1
; DECF INDF,F
;
; BCF RxBufEmptyFlags,6
; RETURN
;
;------------------------------------------------------------------------------------------------
; CTxPipe is not full (or has not yet been detected as being full) and ctx_state is not idle --
; drive the command interpreter Tx state machine through 1 state/iteration (each state/iteration
; typically produces one output character)
process_command_tx INCF CTxPipeWptr,W ;tentatively increment CTxPipe write pointer (to detect a full pipe)
MOVWF TentativeCTxWptr
SUBLW CTxPipeEnd
MOVLW CTxPipe
BTFSC STATUS,Z
MOVWF TentativeCTxWptr
MOVFW CTxPipeRptr
SUBWF TentativeCTxWptr,W
BTFSC STATUS,Z
GOTO ctx_pipe_is_full
MOVLW HIGH CTxJmpTbl
MOVWF PCLATH
DECF ctx_state,W ;ctx_state = 1 is the first entry in the table
ADDLW LOW CTxJmpTbl
BTFSC STATUS,C
INCF PCLATH,F
MOVWF PCL ;jump to handler for current tx_state
;State-machine state handler takes over from here ...
;
;The context for the state handlers is as follows:
; 1). The character produced should be written to tCmd ... tCmd will be written to the CTx Pipe below
; 2). Control should be returned to ctx_done (below) if a character was written, ctx_null if a
; character was not written, or ctx_done_noxs if the checksums should not be updated
ctx_done MOVFW tCmd
call update_ctx_checksums
ctx_done_noxs BCF Flags2,CTxPipeEmpty
Move FSR,CTxPipeWptr
Move INDF,tCmd
Move CTxPipeWptr,TentativeCTxWptr
INCF CTxPipeCount,F
;------ Testing -- printf( "txc %.2X s %.2X xs %.2X cl %.2X ch %.2X\n", tCmd, ctx_state, tCmdxs, tCmdcl, tCmdch );
; to the auxiliary port at address 6
;
; MOVLW 't'
; CALL WriteRxBuf6
; MOVLW 'x'
; CALL WriteRxBuf6
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF tCmd,W
; CALL WriteNybRB6
; MOVF tCmd,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 's'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF ctx_state,W
; CALL WriteNybRB6
; MOVF ctx_state,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'x'
; CALL WriteRxBuf6
; MOVLW 's'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF tCmdxs,W
; CALL WriteNybRB6
; MOVF tCmdxs,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW 'l'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF tCmdcl,W
; CALL WriteNybRB6
; MOVF tCmdcl,W
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW 'h'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; SWAPF tCmdch,W
; CALL WriteNybRB6
; MOVF tCmdch,W
; CALL WriteNybRB6
; MOVLW 13
; CALL WriteRxBuf6
; MOVLW 10
; CALL WriteRxBuf6
;
ctx_null GOTO mainloop
ctx_pipe_is_full BSF Flags1,CTxPipeFull
GOTO mainloop
;-----------------------------------------------------------------------------------
; Set blink code
; Higher blink codes are assumed to be more severe errors than lower
; blink codes so a higher code will not be replaced by a lower code.
set_blink_code DouseStatus
MOVWF temp
SUBWF led_code,W
BTFSC STATUS,C
RETURN
MOVFW temp
MOVWF led_code
RETURN
;------------------------------------------------------------------------------------
; Apply W to receive checksums
update_rx_checksums xorwf r232xs,F ;update plain checksum
xorwf r232cl,F ;update cyclic checksum
addwf r232ch,F
bcf STATUS,C
rrf r232ch,F
btfsc r232cl,0
bsf r232ch,7
rrf r232cl,F
movlw 0x31
addwf r232cl,F
btfsc STATUS,C
incf r232ch,F
movlw 0x03
addwf r232ch,F
return
;------------------------------------------------------------------------------------
; Apply W to transmit checksums
update_tx_checksums xorwf t232xs,F ;update plain checksum
xorwf t232cl,F ;update cyclic checksum xor w with low byte
addwf t232ch,F ;add w to high byte
bcf STATUS,C ;clear status carry
rrf t232ch,F ;rotate high right through carry
btfsc t232cl,0 ;t232.0 set?
bsf t232ch,7 ; set t232.15
rrf t232cl,F ;rotate low byte right through carry
movlw 0x31
addwf t232cl,F ;add 31h to low byte
btfsc STATUS,C ;Carry?
incf t232ch,F ; inc high byte
movlw 0x03
addwf t232ch,F ;add 3h to high byte
return
;------------------------------------------------------------------------------------
; Apply W to command-interpreter receive checksums
update_crx_checksums xorwf rCmdxs,F ;update plain checksum
xorwf rCmdcl,F ;update cyclic checksum
addwf rCmdch,F
bcf STATUS,C
rrf rCmdch,F
btfsc rCmdcl,0
bsf rCmdch,7
rrf rCmdcl,F
movlw 0x31
addwf rCmdcl,F
btfsc STATUS,C
incf rCmdch,F
movlw 0x03
addwf rCmdch,F
return
;------------------------------------------------------------------------------------
; Apply W to command-interpreter transmit checksums
update_ctx_checksums xorwf tCmdxs,F ;update plain checksum
xorwf tCmdcl,F ;update cyclic checksum
addwf tCmdch,F
bcf STATUS,C
rrf tCmdch,F
btfsc tCmdcl,0
bsf tCmdch,7
rrf tCmdcl,F
movlw 0x31
addwf tCmdcl,F
btfsc STATUS,C
incf tCmdch,F
movlw 0x03
addwf tCmdch,F
return
;################################################################################################
; SRAM interface subroutines
;---------------------------------------------------------------------------
; Read one byte from SRAM. Call with FSR pointing to the low byte of the
; address and FSR + 1 pointing to the high byte of the address. A16 should
; be set by the caller (it is not changed).
;
; The address pin assignment is arbitrary. All of the SRAM interface
; subroutines adhere to the convention that the latched address lines
; are A0..7 and the direct lines are A8..15.
;
; The byte read is returned in W.
;
; Time = 17 instruction cycles (3.4us) including call and return
;
ReadMem Move PORTD,INDF
ClrMemAClk
INCF FSR,F
SetMemAClk
Move PORTD,INDF
SetMemCE1
SetMemOE ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
DECF FSR,F
MOVFW PORTB
ClrMemOE
ClrMemCE1
RETURN
;---------------------------------------------------------------------------
; Write one byte to SRAM. Call with FSR pointing to the low byte of the
; address and FSR + 1 pointing to the high byte of the address. A16 should
; be set by the caller (it is not changed). The byte to write should be
; in the W register.
;
; The W register is modified (contains high byte of address) upon return.
;
; Time = 23 instruction cycles (4.6us) including call and return
;
WriteMem MOVWF PORTB
Move PORTD,INDF
ClrMemAClk
INCF FSR,F
SetMemAClk
Move PORTD,INDF
Bank1
CLRF TRISB
Bank0
SetMemCE1
SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
DECF FSR,F
ClrMemWr
ClrMemCE1
Bank1
DECF TRISB,F
Bank0
RETURN
;---------------------------------------------------------------------------
; Write one byte to the transmit buffer.
;
; Updates enqueue pointer.
; Sets Flags1:TxBufOverflow and doesn't move enqueue pointer if the enqueue
; pointer would lap the dequeue pointer when moved.
;
; Modifies FSR
;
WriteTxBuf MOVWF PORTB
SetMemA16
Move PORTD,Txwl
ClrMemAClk
MOVFW Txwh
SetMemAClk
MOVWF PORTD
Movlf FSR,TRISB
CLRF INDF
SetMemCE1
SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
INCF Txwl,F ;this pointer needs to be incremented sooner or later and here is a good place to do it
BTFSC STATUS,Z ;since something needs to be inserted between the SetMemWr and the ClrMemWr
INCF Txwh,F
ClrMemWr
ClrMemCE1
DECF INDF,F
MOVFW Txwl ;Check for buffer overflow
XORWF Txrl,W
BTFSS STATUS,Z
RETURN
MOVFW Txwh
XORWF Txrh,W
BTFSS STATUS,Z
RETURN
BSF Flags1,TxBufOverflow ;The buffer did overflow
MOVLW 1
SUBWF Txwl,F
BTFSS STATUS,C
SUBWF Txwh,F
RETURN
;---------------------------------------------------------------------------
; ReadTxBuf is located on high page
;---------------------------------------------------------------------------
; WriteRxBuf is located on high page
;---------------------------------------------------------------------------
; Read one byte from one of the receive buffers.
;
; Precondition:
;
; RxBufIndex contains SlaveAddress * 2.
; Note that SlaveAddress = 7 for the control channel.
;
; RxBufMask contains POW(2, SlaveAddress).
;
; The dequeue pointer for the buffer (pointer pair at Rx0rl + RxBufIndex)
; points to the next location within the buffer to be read.
;
;
; Postcondition:
;
; W contains the byte read from [Rx0rl + RxBufIndex].
;
; The dequeue pointer for the indexed receive buffer has been incremented.
;
; RxBufEmptyFlags has been OR'd with RxBufMask (bit corresponding to the
; indexed slave has been set) if the dequeue ptr equals the enqueue ptr.
;
;
; Modifies:
; FSR
; mem_temp
; mem_temp2
ReadRxBuf ClrMemA16
MOVLW Rx0rl
ADDWF RxBufIndex,W
MOVWF FSR
Move PORTD,INDF
MOVWF mem_temp ;copy dequeue pointer to mem_temp2:mem_temp
ClrMemAClk
INCF FSR,F
MOVFW INDF
MOVWF mem_temp2
SetMemAClk
MOVWF PORTD
SetMemCE1
SetMemOE ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
INCF mem_temp,F ;increment mem_temp2:mem_temp and copy back to dequeue pointer
BTFSS STATUS,Z
GOTO rrxbnboc
INCF mem_temp2,F
MOVLW 0X1F
ANDWF mem_temp2,W
BTFSS STATUS,Z ;was there a carry from bit 4 to bit 5 ? (high 3 bits are buffer address ... low 5 are offset)
GOTO rrxbnboc
DECF mem_temp2,F ; yes, clear low 5 bits but leave buffer address the same
MOVLW 0XE0
ANDWF mem_temp2,F
rrxbnboc Move INDF,mem_temp2 ;copy incremented pointer to dequeue pointer
DECF FSR,F
Move INDF,mem_temp
MOVLW Rx0wl ;check for empty buffer
ADDWF RxBufIndex,W
MOVWF FSR
MOVFW INDF
XORWF mem_temp,W
BTFSS STATUS,Z
GOTO no_empty_readrxbuf
INCF FSR,F
MOVFW INDF
XORWF mem_temp2,W
BTFSS STATUS,Z
GOTO no_empty_readrxbuf
MOVFW RxBufMask
IORWF RxBufEmptyFlags,F
no_empty_readrxbuf MOVFW PORTB ;end the memory read cycle
ClrMemOE
ClrMemCE1
RETURN
;***********************************************************************************************
; ROM page break
;***********************************************************************************************
org 0x800 ;high page of ROM
;---------------------------------------------------------------------------------------------------------------------
; Rx state machine dispatch table and handlers
;
; This table is used to jump to the receive character state-machine state handler for a given rx_state.
; The first entry corresponds to rx_state = 0, the second entry to rx_state = 1, etc.
;
;
; The context for the state handlers is as follows:
;
; 1). The r232 register contains the next character from the input stream
;
; 2). Control should be returned to rx_done. Since rx_done is on a different ROM page, each handler
; should jump to return_from_rx ... this code fragment will set the page bits and jump to rx_done.
;
; 3). Additional labels are provided upstream from return_from_rx which perform commonly executed
; tasks prior to returning as a convenience and to save space in ROM
;
; 4). The normal return methods update the checksums. If the checksums need to be left alone then
; use the _noxs versions of the returns.
clear_rx_state CLRF rx_state
GOTO return_from_rx
next_rx_state INCF rx_state,W
set_rx_state MOVWF rx_state
return_from_rx LowPage
goto rx_done
clear_rx_state_noxs CLRF rx_state ;no checksum update versions of returns
GOTO return_from_rx_noxs
next_rx_state_noxs INCF rx_state,W
set_rx_state_noxs MOVWF rx_state
return_from_rx_noxs LowPage
goto rx_done_noxs
;
; This dispatch table must be located in same ROM page as the handlers
RxJmpTbl GOTO RX_0
GOTO RX_1
GOTO RX_2
GOTO RX_3
GOTO RX_4
GOTO RX_5
GOTO RX_6
GOTO RX_7
GOTO RX_8
rxs0 CLRF rx_state ;jump here to reset Rx state and test r232 from state 0
RX_0 MOVFW r232 ;1st byte of packet header
XORLW '@'
BTFSS STATUS,Z
GOTO return_from_rx
GOTO next_rx_state
RX_1 MOVFW r232 ;2nd byte of packet header
XORLW 'P'
BTFSS STATUS,Z
GOTO rxs0
CLRF r232xs
CLRF r232cl
CLRF r232ch
GOTO next_rx_state_noxs
RX_2 SWAPF r232,W ;address/length descriptor
MOVWF rx_substate
RRF rx_substate,F
MOVLW 0X07
ANDWF rx_substate,F ;rx_substate = address
MOVLW 0X1F
ANDWF r232,W
ADDLW 1
MOVWF rx_substate2 ;rx_substate2 = length
MOVLW Rx0wl ;RxTwh:RxTwl = buffer enqueue pointer
ADDWF rx_substate,W ; used later by WriteRxBuf
ADDWF rx_substate,W
MOVWF FSR
MOVFW INDF
MOVWF RxTwl
INCF FSR,F
MOVFW INDF
MOVWF RxTwh
GOTO next_rx_state
RX_3 MOVFW r232
MOVWF rx_substate3 ;rx_substate3 = low byte of serial number
GOTO next_rx_state
;serial number ... high byte
;Lost packet count = (packet serial number - expected serial number)
;expected serial number = RxSerial1:RxSerial0 + 1
;Stick lost packet count in rx_substate4:rx_substate_3 ...
;if checksum passes then add it to total lost packet count and add it (+1) to RxSerial1:RxSerial0
RX_4 Move rx_substate4,r232 ;high byte of serial number
COMF RxSerial0,F ;take 1's compliment and add... same result as adding 1 then subtracting
COMF RxSerial1,F ; or adding 1 then taking 2's compliment and adding
MOVFW RxSerial0
ADDWF rx_substate3,F
BTFSC STATUS,Z
INCF rx_substate4,F
MOVFW RxSerial1
ADDWF rx_substate4,F
GOTO next_rx_state
;Receive one databyte ... note that data has been scrambled and must be unscrambled:
;
; The body contains the data (1 to 32 bytes, as specified in the header).
;
; The data bytes themselves are modified via the following algorithm: (using C syntax)
; output_byte = input_byte ^ (cyclic_checksum & 0xff);
; apply_to_checksums( input_byte );
;
; To restore the data to its original value:
; input_byte = output_byte ^ (cyclic_checksum & 0xff);
; apply_to_checksums( input_byte );
;
; This modification of the data is done to prevent packets being sent to cascaded
; multiplexers from being misinterpreted as packets destined for this multiplexer if the
; receive logic gets out of phase.
RX_5 MOVFW r232cl ;restore original databyte from scrambled value
XORWF r232,W
MOVWF r232 ;r232 will be applied to the checksums upon return
CALL WriteRxBuf
DECFSZ rx_substate2,F
GOTO return_from_rx
GOTO next_rx_state
;Checksums
RX_6 MOVFW r232xs ;plain checksum
XORWF r232,W
BTFSC STATUS,Z
GOTO next_rx_state_noxs
GOTO rxs0
RX_7 MOVFW r232cl ;low byte of cyclic checksum
XORWF r232,W
BTFSC STATUS,Z
GOTO next_rx_state_noxs
GOTO rxs0
RX_8 MOVFW r232ch ;high byte of cyclic checksum
XORWF r232,W
BTFSS STATUS,Z
GOTO rxs0
;All checksums passed ... the packet was good
;add current missing packet count to accumulator
MOVFW rx_substate3
ADDWF RxBufMissCount0,F
MOVLW 1
BTFSC STATUS,C
ADDWF RxBufMissCount1,F
BTFSC STATUS,C
ADDWF RxBufMissCount2,F
BTFSC STATUS,C
ADDWF RxBufMissCount3,F
MOVFW rx_substate4
ADDWF RxBufMissCount1,F
MOVLW 1
BTFSC STATUS,C
ADDWF RxBufMissCount2,F
BTFSC STATUS,C
ADDWF RxBufMissCount3,F
;add current missing packet count + 1 to the Rx serial number to bring RxSerial1:RxSerial0 up to date
;(missing packet count is the only information about the packet's serial number saved)
MOVFW rx_substate3
ADDWF RxSerial0,F
BTFSC STATUS,C
INCF RxSerial1,F
MOVFW rx_substate4
ADDWF RxSerial1,F
;Commit the packet to the buffer by copying the tentative write pointer to the actual enqueue pointer
MOVLW Rx0wl
ADDWF rx_substate,W
ADDWF rx_substate,W
MOVWF FSR
Move INDF,RxTwl
INCF FSR,F
Move INDF,RxTwh
;Clear the "buffer empty" flag for the receive buffer to which the packet was addressed
Movlf rx_substate2,0XFE
MOVF rx_substate,F
BTFSC STATUS,Z
GOTO xtlprrbefmrxp
lprrbefmrxp BSF STATUS,C
RLF rx_substate2,F
DECFSZ rx_substate,F
GOTO lprrbefmrxp
xtlprrbefmrxp MOVFW rx_substate2
ANDWF RxBufEmptyFlags,F
GOTO clear_rx_state
;---------------------------------------------------------------------------------------------------------------------
; Tx state machine dispatch table and handlers
;
; This table is used to jump to the transmit character state-machine state handler for a given tx_state.
; The first entry corresponds to tx_state = 1, the second entry to tx_state = 2, etc.
; Note that there is no entry for tx_state = 0 since that is the idle state.
;
;
;The context for the state handlers is as follows:
;
; 1). The character generated should be written to t232.
;
; 2). Control should be returned to tx_done if an output character was written. Since tx_done is on a
; different ROM page, each handler should jump to return_from_tx ... this code fragment will set the
; page bits and jump to tx_done.
;
; 3). Control should be returned to tx_null if a character was not written. Each handler should jump
; to null_tx_return when done if no output was produced.
;
; 4). Additional labels are provided upstream from return_from_tx which perform commonly executed
; tasks prior to returning as a convenience and to save space in ROM
;
; 5). The normal return methods update the checksums. If the checksums need to be left alone then
; use the _noxs versions of the returns.
end_tx CLRF tx_state
GOTO return_from_tx
next_tx_state INCF tx_state,W
set_tx_state MOVWF tx_state
return_from_tx LowPage
goto tx_done
null_tx_return LowPage
goto tx_null
end_tx_noxs CLRF tx_state ;no checksums update versions of returns
GOTO return_from_tx_noxs
next_tx_state_noxs INCF tx_state,W
set_tx_state_noxs MOVWF tx_state
return_from_tx_noxs LowPage
goto tx_done_noxs
;
; This dispatch table must be located in same ROM page as the handlers
TxJmpTbl GOTO TX_1
GOTO TX_2
GOTO TX_3
GOTO TX_4
GOTO TX_5
GOTO TX_6
GOTO TX_7
GOTO TX_8
GOTO TX_9
TX_1 Movlf t232,'@'
GOTO next_tx_state
TX_2 Movlf t232,'P'
GOTO next_tx_state
TX_3 CALL ReadTxBuf ;address/length descriptor
MOVWF t232
SWAPF t232,W
MOVWF tx_substate
RRF tx_substate,F
MOVLW 0X07
ANDWF tx_substate,F ;tx_substate = address
MOVLW 0X1F
ANDWF t232,W
ADDLW 1
MOVWF tx_substate2 ;tx_substate2 = length
CLRF t232xs
CLRF t232cl
CLRF t232ch
GOTO next_tx_state
TX_4 CALL ReadTxBuf ;low byte of serial number
MOVWF t232
GOTO next_tx_state
TX_5 CALL ReadTxBuf ;high byte of serial number
MOVWF t232
GOTO next_tx_state
;Write one databyte ... note that data needs to be scrambled:
;
; The body contains the data (1 to 32 bytes, as specified in the header).
;
; The data bytes themselves are modified via the following algorithm: (using C syntax)
; output_byte = input_byte ^ (cyclic_checksum & 0xff);
; apply_to_checksums( input_byte );
;
; To restore the data to its original value:
; input_byte = output_byte ^ (cyclic_checksum & 0xff);
; apply_to_checksums( input_byte );
;
; This modification of the data is done to prevent packets being sent to cascaded
; multiplexers from being misinterpreted as packets destined for this multiplexer if the
; receive logic gets out of phase.
TX_6 CALL ReadTxBuf
MOVWF temp ;save unmodified databyte for checksum computation
XORWF t232cl,W
MOVWF t232
MOVFW temp
LowPage
CALL update_tx_checksums ;compute checksum using unmodified databyte
HighPage
DECFSZ tx_substate2,F
GOTO return_from_tx_noxs
GOTO next_tx_state_noxs
TX_7 Move t232,t232xs ;plain checksum
GOTO next_tx_state_noxs
TX_8 Move t232,t232cl ;cyclic checksum low byte
GOTO next_tx_state_noxs
TX_9 Move t232,t232ch ;cyclic checksum high byte
MOVLW 1
SUBWF TxBufPCount0,F
BTFSS STATUS,C
SUBWF TxBufPCount1,F
GOTO end_tx_noxs
;---------------------------------------------------------------------------------------------------------------------
; Command-interpreter Rx state machine dispatch table and handlers
;
; This table is used to jump to the receive character state-machine state handler for a given crx_state.
; The first entry corresponds to crx_state = 0, the second entry to crx_state = 1, etc.
;
;
; The context for the state handlers is as follows:
;
; 1). The rCmd register contains the next character from the input stream
;
; 2). Control should be returned to crx_done. Since crx_done is on a different ROM page, each handler
; should jump to return_from_crx ... this code fragment will set the page bits and jump to crx_done.
;
; 3). Additional labels are provided upstream from return_from_crx which perform commonly executed
; tasks prior to returning as a convenience and to save space in ROM
;
; 4). The normal return methods update the checksums. If the checksums need to be left alone then
; use the _noxs versions of the returns.
clear_crx_state CLRF crx_state
GOTO return_from_crx
next_crx_state INCF crx_state,W
set_crx_state MOVWF crx_state
return_from_crx LowPage
goto crx_done
clear_crx_state_noxs CLRF crx_state ;no checksum versions of returns
GOTO return_from_crx_noxs
next_crx_state_noxs INCF crx_state,W
set_crx_state_noxs MOVWF crx_state
return_from_crx_noxs LowPage
goto crx_done_noxs
;
; This dispatch table must be located in same ROM page as the handlers
CRxJmpTbl GOTO CRX_0
GOTO CRX_1
GOTO CRX_2
GOTO CRX_3
GOTO CRX_4
GOTO CRX_5
GOTO CRX_6
GOTO CRX_7
GOTO CRX_8
GOTO CRX_9
GOTO CRX_10
GOTO CRX_11
GOTO CRX_12
GOTO CRX_13
GOTO CRX_14
GOTO CRX_15
GOTO CRX_16
GOTO CRX_17
GOTO CRX_18
GOTO CRX_19
GOTO CRX_20
crxs0 CLRF crx_state ;jump here to reset CRX state and test rCmd from state 0
CRX_0 MOVFW rCmd
XORLW '@'
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO return_from_crx_noxs
CRX_1 MOVFW rCmd
XORLW 'R'
BTFSC STATUS,Z
GOTO next_crx_state_noxs ;"read" command
XORLW 'W' ^ 'R'
BTFSC STATUS,Z
GOTO SET_CRX_7
XORLW 'B' ^ 'W'
BTFSS STATUS,Z
GOTO crxs0
MOVLW 16
GOTO set_crx_state_noxs ;"baud rate" command
SET_CRX_7 MOVLW 7 ;"write" command
GOTO set_crx_state_noxs
;------ Read command ------
CRX_2 MOVLW 0X1C ;Address/length byte -- verify that bits 2..4 are zeros
ANDWF rCmd,W
BTFSS STATUS,Z
GOTO crxs0
SWAPF rCmd,W
Bank1
MOVWF RegXferMCUAddr ;MCU address is in high 3 bits
RRF RegXferMCUAddr,F
MOVLW 7
ANDWF RegXferMCUAddr,F
Bank0
MOVLW 3
ANDWF rCmd,W
ADDLW 1
Bank1
MOVWF RegXferLen
Bank0
CLRF rCmdxs ;clear checksums ... checksum will be taken over addr/len byte
CLRF rCmdcl ; (this byte) and register start address (next byte)
CLRF rCmdch
GOTO next_crx_state
CRX_3 MOVFW rCmd ;register start address
Bank1
MOVWF RegXferAddr
Bank0
GOTO next_crx_state
;Checksums of addr/len and register start address bytes
CRX_4 MOVFW rCmdxs ;plain checksum
XORWF rCmd,W
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO crxs0
CRX_5 MOVFW rCmdcl ;low byte of cyclic checksum
XORWF rCmd,W
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO crxs0
CRX_6 MOVFW rCmdch ;high byte of cyclic checksum
XORWF rCmd,W
BTFSS STATUS,Z
GOTO crxs0
;All checksums passed ... the packet was good ... generate a response
;At this point
; RegXferMCUAddr holds the MCU address,
; RegXferAddr holds the register address,
; RegXferLen holds the length
; rCmdxs, rCmdcl, and rCmdch hold the checksums of the command packet (which must be echoed in the reply)
Move tCmdxs,rCmdxs ;copy checksums for reply
Move tCmdcl,rCmdcl
Move tCmdch,rCmdch
MOVLW 7
Bank1
XORWF RegXferMCUAddr,W
Bank0
BTFSC STATUS,Z
GOTO REGREAD_MASTER ;read from this MCU's register space
;Read from a slave MCU's register space
;Note that if a read command specifies an address for which no slave is installed then the
;firmware will crash. It is the host computer's responsibility to check the installed slaves
;status byte and not request any transfers to/from non-existent slave MCUs.
BTFSS Flags1,SlaveSelected ;is a slave addressed?
GOTO nus_rreg
MOVFW SlaveAddress
BusChipSelect ;tell last slave to "unlisten"
BCF Flags1,SlaveSelected
nus_rreg Bank1
MOVF RegXferMCUAddr,W
Bank0
IORLW 0X80
BusChipSelect ;address the slave specified
MOVLW WordBufAddr
BusWriteIAddr
Bank1
MOVF RegXferAddr,W
Bank0
BusWriteIndirect
Bank1
MOVF RegXferLen,W
Bank0
BusWriteIndirect
MOVLW 0
BusWriteIAddr ;command the slave MCU to perform a read transfer to the Word Buffer
MOVLW WordBuf0
BusWriteIAddr
BusReadIndirect
Bank1
MOVWF RegXfer0 ;move data into RegXfer 0..3
Bank0
BusReadIndirect
Bank1
MOVWF RegXfer1
Bank0
BusReadIndirect
Bank1
MOVWF RegXfer2
Bank0
BusReadIndirect
Bank1
MOVWF RegXfer3
MOVF RegXferMCUAddr,W
Bank0
BusChipSelect ;"unlisten" the slave
FINISH_CRX_READ MOVLW 1 ;CTx state for response to read command
MOVWF ctx_state
GOTO clear_crx_state_noxs
;Read a block of registers from this MCU's register space
REGREAD_MASTER Bank1
MOVF RegXferAddr,W
Bank0
MOVWF FSR
MOVLW HIGH WBRTT ;use jump-table to read n bytes to buffer in right-justified fashion
MOVWF PCLATH
Bank1
MOVF RegXferLen,W
Bank0
ADDLW LOW WBRTT
BTFSC STATUS,C
INCF PCLATH,F
ClrI
MOVWF PCL
WBRTT GOTO WBR0
GOTO WBR1
GOTO WBR2
GOTO WBR3
;WBR4
MOVF INDF,W
Bank1
MOVWF RegXfer0
Bank0
INCF FSR,F
WBR3 MOVF INDF,W
Bank1
MOVWF RegXfer1
Bank0
INCF FSR,F
WBR2 MOVF INDF,W
Bank1
MOVWF RegXfer2
Bank0
INCF FSR,F
WBR1 MOVF INDF,W
Bank1
MOVWF RegXfer3
Bank0
WBR0 SetI
GOTO FINISH_CRX_READ
;------ Write Command ------
CRX_7 MOVLW 0X1C ;Address/length byte -- verify that bits 2..4 are zeros
ANDWF rCmd,W
BTFSS STATUS,Z
GOTO crxs0
SWAPF rCmd,W
Bank1
MOVWF RegXferMCUAddr ;MCU address is in high 3 bits
RRF RegXferMCUAddr,F
MOVLW 7
ANDWF RegXferMCUAddr,F
Bank0
MOVLW 3
ANDWF rCmd,W
ADDLW 1
Bank1
MOVWF RegXferLen
Bank0
CLRF rCmdxs ;clear checksums
CLRF rCmdcl
CLRF rCmdch
GOTO next_crx_state
CRX_8 MOVFW rCmd ;register start address
Bank1
MOVWF RegXferAddr
MOVF RegXferLen,W ;block length (1..4)
Bank0
SUBLW 5
ADDWF crx_state,F ;State 9 for 4 bytes, state 10 for 3 bytes, etc.
GOTO return_from_crx
CRX_9 MOVF rCmd,W
Bank1
MOVWF RegXfer0
Bank0
GOTO next_crx_state
CRX_10 MOVF rCmd,W
Bank1
MOVWF RegXfer1
Bank0
GOTO next_crx_state
CRX_11 MOVF rCmd,W
Bank1
MOVWF RegXfer2
Bank0
GOTO next_crx_state
CRX_12 MOVF rCmd,W
Bank1
MOVWF RegXfer3
Bank0
GOTO next_crx_state
CRX_13 MOVFW rCmdxs ;plain checksum
XORWF rCmd,W
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO crxs0
CRX_14 MOVFW rCmdcl ;low byte of cyclic checksum
XORWF rCmd,W
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO crxs0
CRX_15 MOVFW rCmdch ;high byte of cyclic checksum
XORWF rCmd,W
BTFSS STATUS,Z
GOTO crxs0
;All checksums passed ... the packet was good ... write the data and then generate a response
;At this point
; RegXferMCUAddr holds the MCU address,
; RegXferAddr holds the register address,
; RegXferLen holds the length
; RegXfer0 .. RegXfer3 hold the right-justified block of registers
; rCmdxs, rCmdcl, and rCmdch hold the checksums of the command packet (which must be echoed in the reply)
Move tCmdxs,rCmdxs ;copy checksums for reply
Move tCmdcl,rCmdcl
Move tCmdch,rCmdch
MOVLW 7
Bank1
XORWF RegXferMCUAddr,W
Bank0
BTFSC STATUS,Z
GOTO REGWRITE_MASTER ;write to this MCU's register space
;Write to a slave MCU's register space
;Note that if a read command specifies an address for which no slave is installed then the
;firmware will crash. It is the host computer's responsibility to check the installed slaves
;status byte and not request any transfers to/from non-existent slave MCUs.
BTFSS Flags1,SlaveSelected ;is a slave addressed?
GOTO nus_wreg
MOVFW SlaveAddress
BusChipSelect ;tell last slave to "unlisten"
BCF Flags1,SlaveSelected
nus_wreg
;------ Testing -- printf( "Block Write %.2X addr %.2X len %.2X %.2X%.2X%.2X%.2X\n", RegXferMCUAddr, RegXferAddr, RegXferLen, RegXfer3, RegXfer2, RegXfer1, RegXfer0 );
; to the auxiliary port at address 6
;
; LowPage
; MOVLW 'B'
; CALL WriteRxBuf6
; MOVLW 'l'
; CALL WriteRxBuf6
; MOVLW 'o'
; CALL WriteRxBuf6
; MOVLW 'c'
; CALL WriteRxBuf6
; MOVLW 'k'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW 'W'
; CALL WriteRxBuf6
; MOVLW 'r'
; CALL WriteRxBuf6
; MOVLW 'i'
; CALL WriteRxBuf6
; MOVLW 't'
; CALL WriteRxBuf6
; MOVLW 'e'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; Bank1
; SWAPF RegXferMCUAddr,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXferMCUAddr,W
; Bank0
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'a'
; CALL WriteRxBuf6
; MOVLW 'd'
; CALL WriteRxBuf6
; MOVLW 'd'
; CALL WriteRxBuf6
; MOVLW 'r'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; Bank1
; SWAPF RegXferAddr,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXferAddr,W
; Bank0
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; MOVLW 'l'
; CALL WriteRxBuf6
; MOVLW 'e'
; CALL WriteRxBuf6
; MOVLW 'n'
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; Bank1
; SWAPF RegXferLen,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXferLen,W
; Bank0
; CALL WriteNybRB6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
; MOVLW ' '
; CALL WriteRxBuf6
;
; Bank1
; SWAPF RegXfer3,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXfer3,W
; Bank0
; CALL WriteNybRB6
; Bank1
; SWAPF RegXfer2,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXfer2,W
; Bank0
; CALL WriteNybRB6
; Bank1
; SWAPF RegXfer1,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXfer1,W
; Bank0
; CALL WriteNybRB6
; Bank1
; SWAPF RegXfer0,W
; Bank0
; CALL WriteNybRB6
; Bank1
; MOVF RegXfer0,W
; Bank0
; CALL WriteNybRB6
;
; MOVLW 13
; CALL WriteRxBuf6
; MOVLW 10
; CALL WriteRxBuf6
; HighPage
;
Bank1
MOVF RegXferMCUAddr,W
Bank0
IORLW 0X80
BusChipSelect ;address the slave specified
MOVLW WordBufAddr
BusWriteIAddr
Bank1
MOVF RegXferAddr,W
Bank0
BusWriteIndirect
Bank1
MOVF RegXferLen,W
Bank0
BusWriteIndirect
Bank1
MOVF RegXfer0,W ;move data from RegXfer 0..3
Bank0
BusWriteIndirect
Bank1
MOVF RegXfer1,W
Bank0
BusWriteIndirect
Bank1
MOVF RegXfer2,W
Bank0
BusWriteIndirect
Bank1
MOVF RegXfer3,W
Bank0
BusWriteIndirect
MOVLW 0X80
BusWriteIAddr ;command the slave MCU to perform a write transfer from the Word Buffer
Bank1
MOVF RegXferMCUAddr,W
Bank0
BusChipSelect ;"unlisten" the slave
FINISH_CRX_WRITE MOVLW 13 ;CTx state for response to write command
MOVWF ctx_state
GOTO clear_crx_state_noxs
;Write a block of registers to this MCU's register space
REGWRITE_MASTER Bank1
MOVF RegXferAddr,W
Bank0
MOVWF FSR
MOVLW HIGH WBWTT ;use jump-table to write n bytes from buffer in right-justified fashion
MOVWF PCLATH
Bank1
MOVF RegXferLen,W
Bank0
ADDLW LOW WBWTT
BTFSC STATUS,C
INCF PCLATH,F
ClrI
MOVWF PCL
WBWTT GOTO WBW0
GOTO WBW1
GOTO WBW2
GOTO WBW3
;WBW4
Bank1
MOVF RegXfer0,W
Bank0
MOVWF INDF
INCF FSR,F
WBW3 Bank1
MOVF RegXfer1,W
Bank0
MOVWF INDF
INCF FSR,F
WBW2 Bank1
MOVF RegXfer2,W
Bank0
MOVWF INDF
INCF FSR,F
WBW1 Bank1
MOVF RegXfer3,W
Bank0
MOVWF INDF
INCF FSR,F
WBW0 SetI
GOTO FINISH_CRX_WRITE
;------ Baud rate change command ------
CRX_16 MOVLW 0X6C ;TXSTA value XOR'd with 0X6C
XORWF rCmd,F
MOVF rCmd,W
Bank1
MOVWF RegXfer0 ;stash TXSTA value here for now
Bank0
CLRF rCmdxs ;clear checksums
CLRF rCmdcl
CLRF rCmdch
GOTO next_crx_state
CRX_17 MOVLW 0X35 ;SPBRG value XOR'd with 0X35
XORWF rCmd,F
MOVF rCmd,W
Bank1
MOVWF RegXfer1 ;stash SPBRG value here for now
Bank0
GOTO next_crx_state
;Checksums of TXSTA and SPBRG
CRX_18 MOVFW rCmdxs ;plain checksum
XORWF rCmd,W
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO crxs0
CRX_19 MOVFW rCmdcl ;low byte of cyclic checksum
XORWF rCmd,W
BTFSC STATUS,Z
GOTO next_crx_state_noxs
GOTO crxs0
CRX_20 MOVFW rCmdch ;high byte of cyclic checksum
XORWF rCmd,W
BTFSS STATUS,Z
GOTO crxs0
;All checksums passed ... the packet was good ... reset baud rate
CLRF RCSTA ;shut off serial port so its logic resets
BTFSS Flags1,SlaveSelected ;is a slave addressed?
GOTO nus_rbr
MOVFW SlaveAddress
BusChipSelect ;tell last slave to "unlisten"
BCF Flags1,SlaveSelected
nus_rbr LowPage
GOTO RESET_BAUD
;---------------------------------------------------------------------------------------------------------------------
; Command interpreter Tx state machine dispatch table and handlers
;
; This table is used to jump to the transmit character state-machine state handler for a given ctx_state.
; The first entry corresponds to ctx_state = 1, the second entry to ctx_state = 2, etc.
; Note that there is no entry for ctx_state = 0 since that is the idle state.
;
;
;The context for the state handlers is as follows:
;
; 1). The output character should be written to tCmd
;
; 2). Control should be returned to ctx_done if an output character was written. Since ctx_done is on a
; different ROM page, each handler should jump to return_from_ctx ... this code fragment will set the
; page bits and jump to ctx_done.
;
; 3). Control should be returned to ctx_null if a character was not written. Each handler should jump
; to null_ctx_return when done if no output was produced.
;
; 4). Additional labels are provided upstream from return_from_ctx which perform commonly executed
; tasks prior to returning as a convenience and to save space in ROM
;
; 5). The normal return methods update the checksums. If the checksums need to be left alone then
; use the _noxs versions of the returns.
end_ctx CLRF ctx_state
GOTO return_from_ctx
next_ctx_state INCF ctx_state,W
set_ctx_state MOVWF ctx_state
return_from_ctx LowPage
goto ctx_done
null_ctx_return LowPage
goto ctx_null
end_ctx_noxs CLRF ctx_state ;no-checksums versions of returns
GOTO return_from_ctx_noxs
next_ctx_state_noxs INCF ctx_state,W
set_ctx_state_noxs MOVWF ctx_state
return_from_ctx_noxs LowPage
goto ctx_done_noxs
;
; This dispatch table must be located in same ROM page as the handlers
CTxJmpTbl GOTO CTX_1
GOTO CTX_2
GOTO CTX_3
GOTO CTX_4
GOTO CTX_5
GOTO CTX_6
GOTO CTX_7
GOTO CTX_8
GOTO CTX_9
GOTO CTX_10
GOTO CTX_11
GOTO CTX_12
GOTO CTX_13
GOTO CTX_14
;-----------
;Send "read registers" response
CTX_1 MOVLW '@'
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_2 MOVLW 'r'
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_3 MOVF tCmdxs,W ;Checksum previously saved from the command packet now being replied to
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_4 MOVF tCmdcl,W
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_5 MOVF tCmdch,W
MOVWF tCmd
CLRF tCmdxs
CLRF tCmdcl
CLRF tCmdch
Bank1
MOVF RegXferLen,W
Bank0
SUBLW 5
ADDWF ctx_state,F ;goto state 6 for 4-byte transfer, 5 for 3-byte transfer, etc.
GOTO return_from_ctx_noxs
CTX_6 Bank1
MOVF RegXfer0,W
Bank0
MOVWF tCmd
GOTO next_ctx_state
CTX_7 Bank1
MOVF RegXfer1,W
Bank0
MOVWF tCmd
GOTO next_ctx_state
CTX_8 Bank1
MOVF RegXfer2,W
Bank0
MOVWF tCmd
GOTO next_ctx_state
CTX_9 Bank1
MOVF RegXfer3,W
Bank0
MOVWF tCmd
GOTO next_ctx_state
CTX_10 MOVF tCmdxs,W ;Checksum of data
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_11 MOVF tCmdcl,W
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_12 MOVF tCmdch,W
MOVWF tCmd
GOTO end_ctx_noxs
;-----------
;Send "write registers" response
CTX_13 MOVLW '@'
MOVWF tCmd
GOTO next_ctx_state_noxs
CTX_14 MOVLW 'w'
MOVWF tCmd
MOVLW 10
GOTO set_ctx_state_noxs ;Send checksums previously saved from the command packet now being replied to
;---------------------------------------------------------------------------
; Read one byte from the transmit buffer.
;
; Updates dequeue pointer.
;
ReadTxBuf SetMemA16
Move PORTD,Txrl
ClrMemAClk
MOVFW Txrh
SetMemAClk
MOVWF PORTD
SetMemCE1
SetMemOE ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
INCF Txrl,F ;this pointer needs to be incremented sooner or later and here is a good place to do it
BTFSC STATUS,Z ;since a delay has to come between asserting OE and reading the data
INCF Txrh,F
MOVFW PORTB
ClrMemOE
ClrMemCE1
RETURN
;---------------------------------------------------------------------------
; Write one byte to one of the receive buffers.
;
; Precondition:
;
; The tentative enqueue pointer (RxTwl, RxTwh) points to the next location
; within the buffer to be written.
;
; W contains the databyte to be written to the buffer.
;
;
; Postcondition:
;
; W has been written to [RxTwh:RxTwl].
;
; The tentative enqueue pointer has been incremented.
;
;
; Modifies:
; FSR (= TRISB)
WriteRxBuf MOVWF PORTB
ClrMemA16
Move PORTD,RxTwl
ClrMemAClk
MOVFW RxTwh
SetMemAClk
MOVWF PORTD
Movlf FSR,TRISB
CLRF INDF
SetMemCE1
SetMemWr ;note -- CE1 and OE are on different ports so its ok to do read-modify-write back-to-back
INCF RxTwl,F ;increment the tentative enqueue pointer
BTFSS STATUS,Z ;Carry out of low byte?
GOTO wrxbuf_noihbp ; no, no need to increment high byte
INCF RxTwh,F
MOVLW 0X1F ;check for a carry from bit 4 to bit 5 (high 3 bits are buffer address ... low 5 are offset)
ANDWF RxTwh,W
BTFSS STATUS,Z ;Carry from bit4 to bit5 ?
GOTO wrxbuf_noihbp ; no, done
DECF RxTwh,F ; yes, restore the high 3 bits and clear the low 5 bits
MOVLW 0XE0
ANDWF RxTwh,F
wrxbuf_noihbp ClrMemWr
ClrMemCE1
DECF INDF,F
RETURN
END
Source Code
Old Source
Code Older
Source Code
Subtree
Local home
Home