*********************************************************
* HDLC Programming Example Using Interrupts             *
*                                                       *
* This code is derived from the HDLC sample code        *
* on page 7-192 of the 68360 UM but uses 4 transmit     *
* and 4 receive BDs along with an interrupt driven      *
* receive routine.  It also makes use of the            *
* address mask                                          *
*********************************************************
* 1) The code sets up SCC2 in HDLC mode                 *
* 2) It sets up 4 transmit buffer descriptors that      *
* that will send data when the transmitter is enabled.  *
* 3) It sets up 4 receive buffer descriptors to receive *
* the incoming data                                     *
* 4) It sets up the vectors to the interrupt handling   *
* routines                                              *
* 5) Finally, it turns on the transmitter and receiver  *
* in local loopback mode                                *
*                                    JS 7/13/94         *
*********************************************************
* If you wish to move this code so that it works on     *
* another SCC, there are quite a few things that need to*
* be done.                                              *
*       1) Change the names of all of the SCC equates   *
*       2) Change the SCC2PB equate to SCCx and set it  *
*          to the correct offset (see page 3-4)         *
*       3) Change the equate for the SCC Vector         *
*       4) Change the port pin setup instructions so    *
*          that they activate the correct pins          *
*       5) Change the SICR register so that the correct *
*          SCC gets clocking                            *
*       6) Change the INIT RX&TX PARAMETERS command so  *
*          that it refers to the correct SCC (p. 7-7)   *
*       7) Change the CIMR setup so that the new SCC    *
*          can generate interrupts                      *
*       8) Change the CISR clearing in the interrupt    *
*          service routine so that it clears the bit    *
*          corresponding to the new SCC                 *
***********************************************************************
*
* Board Specific Equates
INIT    EQU     $0403000        ; QUADS Board DRAM User Space

************************** DUAL PORT RAM ******************************
MBAR    EQU     $03FF00 ; Module Base Address, (MBAR)=REGB
*MBAR   EQU     $03FF04 ; Module Base Address,in SLAVE mode
MBARE   EQU     $03FF08 ; Module Base Address Enable SLAVE
*
***********************************************************************
* The following equates assume that they will be used in register
* indirect addressing.  The address register  should hold the base
* address of the QUICC internal memory.  All system equates are
* offsets into this memory

REGB    EQU     $1000   ; Base address of Internal Registers
SCC2PB  EQU     $0D00   ; SCC2 Protocol Base address

************************ SIM Module registers **************************
MCR     EQU     REGB+$0000      ; Module Configuration
AVR     EQU     REGB+$0008      ; Auto Vector
RSR     EQU     REGB+$0009      ; Reset Status
CLKOCR  EQU     REGB+$000C      ; CLKO Control
PLLCR   EQU     REGB+$0010      ; PLL Control
CDVCR   EQU     REGB+$0014      ; Clock Divider Control
*
PEPAR   EQU     REGB+$0016      ; Port E Pin Assignment
*
GMR     EQU     REGB+$0040      ; Global Memory
MSTAT   EQU     REGB+$0044      ; MEMC Status
*
************************ CPM Module registers *******************
*
*** SDMA
SDSR    EQU     REGB+$051C      ; SDMA Status
SDCR    EQU     REGB+$051E      ; SDMA Configuration Register
SDAR    EQU     REGB+$0520      ; SDMA Address Register
*** CP Interrupt
CICR    EQU     REGB+$0540      ; CP Interrupt Configuration
CIPR    EQU     REGB+$0544      ; CP Interrupt Pending
CIMR    EQU     REGB+$0548      ; CP Interrupt Mask
CISR    EQU     REGB+$054C      ; CP Interrupt In-service
*** Port A, B, C
PADIR   EQU     REGB+$0550      ; Port A Data Direction Assignment
PAPAR   EQU     REGB+$0552      ; Port A Pin Function Assignment
PAODR   EQU     REGB+$0554      ; Port A Open Drain Assignment
PADAT   EQU     REGB+$0556      ; Port A Data Content
*
PBDIR   EQU     REGB+$06B8      ; Port B Data Direction Assignment
PBPAR   EQU     REGB+$06BC      ; Port B Pin Function Assignment
PBODR   EQU     REGB+$06C2      ; Port B Open Drain Assignment
PBDAT   EQU     REGB+$06C4      ; Port B Data Content
*
PCDIR   EQU     REGB+$0560      ; Port C Data Direction Assignment
PCPAR   EQU     REGB+$0562      ; Port C Pin Assignment
PCSO    EQU     REGB+$0564      ; Port C Special Options
PCDAT   EQU     REGB+$0566      ; Port C Data Content
PCINT   EQU     REGB+$0568      ; Port C Interrupt Control
*** CP
CR      EQU     REGB+$05C0      ; CP Command
RCCR    EQU     REGB+$05C4      ; RISC Configuration
RTER    EQU     REGB+$05D6      ; RISC Timer Event Register
RTMR    EQU     REGB+$05DA      ; RISC Timer Mask Register
* BRG 1
BRGC1   EQU     REGB+$05F0      ; BRG1 Configuration
BRGC2	EQU	REGB+$05F4	; BRG2 Configuration
*** SCC 2
* SCC2
GSMR_L2 EQU     REGB+$0620      ; SCC2 General Mode
GSMR_H2 EQU     REGB+$0624      ; SCC2 General Mode
PSMR2   EQU     REGB+$0628      ; SCC2 Protocol Specific Mode
TODR2   EQU     REGB+$062C      ; SCC2 Transmit On Demand
DSR2    EQU     REGB+$062E      ; SCC2 Data Sync
SCCE2   EQU     REGB+$0630      ; SCC2 Event
SCCM2   EQU     REGB+$0634      ; SCC2 Mask
SCCS2   EQU     REGB+$0637      ; SCC2 Status
*** Serial Interface
SIMODE  EQU     REGB+$06E0      ; SI Mode
SIGMR   EQU     REGB+$06E4      ; SI Global Mode
SISTR   EQU     REGB+$06E6      ; SI Status
SICMR   EQU     REGB+$06E7      ; SI Command
SICR    EQU     REGB+$06EC      ; SI Clock Routing
SIRP    EQU     REGB+$06F0      ; SI RAM Pointer
SIRAM   EQU     REGB+$0700      ; SI Routing RAM

************************ END of SIM/CPM Register MAP *******************

************************ SCC Parameters ********************************
****** SCC2
RBASE2  EQU     SCC2PB+$000     ; SCC2 RX BD Base Address
TBASE2  EQU     SCC2PB+$002     ; SCC2 TX BD Base Address
RFCR2   EQU     SCC2PB+$004     ; SCC2 RX Function Code
TFCR2   EQU     SCC2PB+$005     ; SCC2 TX Function Code
MRBLR2  EQU     SCC2PB+$006     ; SCC2 MAX Buffer Length - RX
RSTATE2 EQU     SCC2PB+$008     ; SCC2 RX Internal State
RBPRT2  EQU     SCC2PB+$010     ; SCC2 RX BD Pointer
TSTATE2 EQU     SCC2PB+$018     ; SCC2 TX Internal State
TBPRT2  EQU     SCC2PB+$020     ; SCC2 TX BD Pointer
RCRC2   EQU     SCC2PB+$028     ; SCC2 Temp Receive CRC
TCRC2   EQU     SCC2PB+$02C     ; SCC2 Temp Transmit CRC

************************** Specific Parameters ******************************
* SCC2, HDLC Specific Parameters
C_MASK2 EQU     SCC2PB+$34      ; SCC2 CRC Constant
C_PRES2 EQU     SCC2PB+$38      ; SCC2 CRC Preset
DISFC2  EQU     SCC2PB+$3C      ; SCC2 Discard Frame Counter
CRCEC2  EQU     SCC2PB+$3E      ; SCC2 CRC Error Counter
ABTSC2  EQU     SCC2PB+$40      ; SCC2 Abort Sequence Counter
NMARC2  EQU     SCC2PB+$42      ; SCC2 NonMatch Addr Rx Counter
RETRC2  EQU     SCC2PB+$44      ; SCC2 Frame ReTRansmit Counter
MFLR2   EQU     SCC2PB+$46      ; SCC2 Max Frame Length Register
MAXCNT2 EQU     SCC2PB+$48      ; SCC2 MAX length Counter
RFTHR2  EQU     SCC2PB+$4A      ; SCC2 Receive Frames THreshold
RFCNT2  EQU     SCC2PB+$4C      ; SCC2 Receive Frames Count
HMASK2  EQU     SCC2PB+$4E      ; SCC2 Frame Address Mask (U-D)
HADDR12 EQU     SCC2PB+$50      ; SCC2 Frame Address 1
HADDR22 EQU     SCC2PB+$52      ; SCC2 Frame Address 2
HADDR32 EQU     SCC2PB+$54      ; SCC2 Frame Address 3
HADDR42 EQU     SCC2PB+$56      ; SCC2 Frame Address 4

* Program Specific Equates
MBUFS   EQU     $0020           ; Max Buffer Size = 32 bytes
SCC2VEC EQU     $1d             ; Vector for SCC2 (5 bits)  (page 7-369)
STADDR  EQU     $4000
STADDRL EQU     $00             ; Address of hypothetical receiving station
STADDRH EQU     $40
*********
* Start of Program
*********

        ORG     INIT

*** Get Pointer to Base of DPRAM
        move.l  #7,d0           ; "0111" is the function code for CPU Space
        movec   d0,SFC          ; Store "0111" into both the Src Fcn Code Reg
        movec   d0,DFC          ; and the Dest. Fcn Code Reg
        moves.l MBAR,a0         ; Move MBAR into a0
        move.l  a0,d0
        andi.l  #$ffffe000,d0   ; Extract the base address from MBAR
        move.l  d0,a5           ; Move it to a5 for register indexing

*** Set up SDMA Control Register for SCCs
        move.w  #$0740,SDCR(a5) ; SDMA interrupt service mask = 7,
*                               ;  bus arbit. priority = 4

*** Set up Port A Pins
* to get PA2&3 to be Rxd2 & Txd2, we set PAPAR, PADIR, and PAODR as in
* Table 7-16 on page 7-351.
*                                       ; enable RXD2 & TXD2 pins
        or.w    #$000C,PAPAR(a5)        ; bits 2&3 must be 1
        and.w   #$FFF7,PAODR(a5)        ; bit 3 must be zero
        and.w   #$fff3,PADIR(a5)        ; bits 2&3 must be 0
        

*** Set up Port C pins
* to get PC6&PC7 to be CTS2 and CD2 and PC1 to be RTS2,
* we set PCPAR, PCDIR, and PCODR as in Table 7-18 on page 7-358.
*                                       ; enable CD2,CTS2, and RTS2
        and.w   #$ff3f,PCPAR(a5)        ; bits 6&7 must = 0
        or.w    #$0002,PCPAR(a5)        ; bit 1 must = 1 
        and.w   #$ff3d,PCDIR(a5)        ; bits 1, 6, & 7 must be = 0
        or.w    #$00C0,PCSO(a5)         ; bits 6&7 must = 1


*** Set up BRG1 for SCC2
        move.l  #$0001030A,BRGC1(a5)    ; set brg1 to baud rate 64Kbits/s
*                                        ; CD=$185, EN=1, DIV16=0.
*                                        ; 25Mhz/($185+1)=64,000
*                                        ; and GSMR selects 1x clock
        and.l   #$FFFF00FF,SICR(a5)     ; route brg1 to scc2 and select nmsi
*                                       ; SC2=0, R2CS=0, T2CS=0

*** Set up General SCC Parameters
        move.w  #$0000,RBASE2(a5)       ; Receive BDs start at 0(a5)
        move.w  #$0020,TBASE2(a5)       ; Transmit BDs start at 32(a5) 
        move.b  #$18,RFCR2(a5)  ; mot=1(msbyte first), rx function code = 1000
        move.b  #$18,TFCR2(a5)  ; mot=1(msbyte first), tx function code = 1000
        move.w  #MBUFS,MRBLR2(a5)       ; max. bytes per Buffer = MBUFS

*** Execute the INIT RX & TX Parameters for SCC2
        move.w  #$0041,CR(a5)   ; initialize transmit and receive parameter
*                               ; in the parameter ram of scc2


*** Set up HDLC Specific Parameters
        move.l  #$0000F0B8,C_MASK2(a5)  ; for 16 bit CCITT-CRC
        move.l  #$0000FFFF,C_PRES2(a5)  
        clr.w   DISFC2(a5)              ; clear error counters
        clr.w   CRCEC2(a5)
        clr.w   ABTSC2(a5)
        clr.w   NMARC2(a5)
        clr.w   RETRC2(a5)
        move.w  #$0100,MFLR2(a5)        ; Max Frame Length of 256 bytes
        move.w  #$0001,RFTHR2(a5)       ; Get an interrupt after each frame
        move.w  #$ffff,HMASK2(a5)       ; So that all address bits are checked
        move.w  #$ffff,HADDR12(a5)      ; receive address of $ffff (broadcast address)
        move.w  #STADDR,HADDR22(a5)     ; or address of STADDR (note figure 7-51)
        move.w  #$ffff,HADDR32(a5)      ; dummy
        move.w  #$ffff,HADDR42(a5)      ; dummy



*** Set up the Receive BDs 
* (set the ready bit after the Buffer pointer is set up)
        move.l  a5,a0
        add.w   RBASE2(a5),a0           ; make pointer to Receive BDs
* BD1
        clr.w   2(a0)                   ; initialize length
        move.l  #RXBUF+0*MBUFS,4(a0)    ; pointer to buffer number 0
        move.w  #$9000,(a0)             ; empty, nowrap, int
* BD2
        clr.w   10(a0)                  ; initialize length
        move.l  #RXBUF+1*MBUFS,12(a0)   ; pointer to buffer number 1
        move.w  #$9000,8(a0)            ; empty, nowrap, int
* BD3
        clr.w   18(a0)                  ; initialize length
        move.l  #RXBUF+2*MBUFS,20(a0)   ; pointer to buffer number 2
        move.w  #$9000,16(a0)           ; empty, nowrap, int
* BD4
        clr.w   26(a0)                  ; initialize length
        move.l  #RXBUF+3*MBUFS,28(a0)   ; pointer to buffer number 3
        move.w  #$b000,24(a0)           ; empty, wrap, int

*** Set up Process Receive Data Pointer (part of interrupt service routine)
        move.w  RBASE2(a5),PRDPTR       ; PRDPTR points to BD#1


*** Set up Transmit BDs
* (set the ready bit after the Buffer pointer & length is set up)
        movea.l a5,a0                   ; get pointer to base of DPRAM
        adda.w  TBASE2(a5),a0           ; make pointer to Transmit BDs
* BD1
        move.w  #(TBUF1-TBUF0),2(a0)            ; length 13
        move.l  #TBUF0,4(a0)            ; pointer to buffer #0
        move.w  #$8C00,(a0)             ; ready, nowrap, noint, Xmit CRC, Last
* BD2
        move.w  #(TBUF2-TBUF1),10(a0)           ; length 13
        move.l  #TBUF1,12(a0)           ; pointer to buffer #1
        move.w  #$8C00,8(a0)            ; ready, nowrap, noint, Xmit CRC, Last
* BD3
        move.w  #(TBUF3-TBUF2),18(a0)           ; length 13
        move.l  #TBUF2,20(a0)           ; pointer to buffer #2
        move.w  #$8C00,16(a0)           ; ready, nowrap, noint, Xmit CRC, Last
* BD4
        move.w  #(TBUF4-TBUF3),26(a0)           ; length 13
        move.l  #TBUF3,28(a0)           ; pointer to buffer #3
        move.w  #$aC00,24(a0)           ; ready, nowrap, noint, Xmit CRC, Last



*** Set up Vector Table, Configure SCC2 to be SCCa.
* 
* Set up CICR. (In most situations, the VBA2-0 bits and the IRL2-0 bits
* would be set by the kernal and the SCC Priority bits would be set
* by the SCC control program ("user code")).  The QUADS boot rom
* does not set up this register so this code will do both in separate
* steps.

* kernel step
        move.l  #$00008040,CICR(a5)     ; Int Level=4, Vec Base=2
*                                       ; *note* VEC BASE Must Be >=2

* user steps
        move.l  CICR(a5),d0             ; get CICR
        andi.l  #$0000e0e0,d0           ; mask out all but IRL & VBA
        clr.l   d1
        move.b  d0,d1                   ; store copy of VBA
        ori.l   #$00931f00,d0           ; Priority=4,1,2,3; Std HP
*                                       ; Grouped SCCs
        move.l  d0,CICR(a5)                     

* now calculate the Vector Table Location for SCCa Exception Vector
        ori.b   #SCC2VEC,d1             ; concatenate VBA with Vec Num
        asl.l   #2,d1                   ; create offset into vector table
        movec.l VBR,a4                  ; get Vector Base Register
        move.l  #SCC2INT,0(a4,d1)       ; put address of service routine
*                                       ; into vector table     



*** Final Set up
        move.w  #$ffff,SCCE2(a5)        ; clear scc2 event register
        move.w  #$0009,SCCM2(a5)        ; enable rxf and rxb interrupts
        ori.l   #$20000000,CIMR(a5)     ; Allow SCC2 interrupts
        andi.w  #$f8ff,SR               ; Set CPU IMASK to 0
        
        move.l  #$00000000,GSMR_H2(a5)  ; normal FIFOs
        move.l  #$00000040,GSMR_L2(a5)  ; loopback mode
*                                       ; channel protocol mode = HDLC
        move.w  #$0080,PSMR2(a5)        ; CCITT CRC, Flag Sharing
	move.w	#$7E7E,DSR2(a5)		; required for HDLC mode

        ori.l   #$00000030,GSMR_L2(a5)  ; enable transmitter and receiver

self    bra     self                    ; loop forever

*** When this code is "done", all four TX Buffers will have been sent (and
*thus the Ready Bit in each BD will be clear).  The Rx Routine will "service"
*each Rx BD when the RXF (or RXB) event bit is set in the Event Register.
*The TXB bit does not generate an interrupt but will be set each time the
*TX Buffer is sent.
*Only three of the four frames should be received into the receive buffers
*starting at INIT+$2000.  The third frame has an address that is not
*recognized by the controller due to the configuration of the HADDR entries.


**** SCC2 Interrupt Routine
* The framework for this routine can be found on page D-12 of the 68302 UM
* It has been changed to accomodate CM mode.  The condition for stopping
* is no longer the E bit set (because in CM mode, the E bit is ALWAYS set)
* The BD processing now stops when PRDPTR=RBPRT2.  ***Note that RBPRT2 is
* now an offset into DPRAM rather than the BD number (1..8)

SCC2INT movem.l d0-d2/a0-a2,-(a7)       ; save context registers
        move.w  SCCE2(a5),-(a7) ; temporarily store SCC event 
*                               ; register on stack
        move.w  #$ffff,SCCE2(a5) ; Clear all SCC events
        move.w  (a7),d2         ; get a copy of the register
        andi.w  #9,d2           ; is RXF or RXB set?
        beq     INT1                    
        bsr     REC_BD          ;   if so, process the BDs
INT1    move.l  #$20000000,CISR(a5)     ; clear SCC2 bit in ISR 
        move.w  (a7)+,d2        ; remove SCCE2 from stack
        movem.l (a7)+,d0-d2/a0-a2       ; Restore Context
        rte

REC_BD  move.w  PRDPTR,d0
        move.l  a5,a0           ; REPEAT--UNTIL style loop
        add.w   d0,a0           ; a0 is absolute pointer to unprocessed BD
        move.w  (a0),d0         ; get copy of BD status word
        andi.w  #$00ff,d0       ; save error indicators
        bne     ERR_HAND        ; branch to error handler if errors
*
** Process the received data here
*
CONT    clr.w   2(a0)           ; Clear the length word
        andi.w  #$f200,(a0)     ; clear status bits
        bset.b  #7,(a0)         ; Set the Empty Bit
        btst.b  #5,(a0)         ; Is the wrap bit set?
        bne     WRAP_R          ;  if so, point PRD to RBASE
        addq.w  #$8,PRDPTR      ; else increment PRDPTR to next BD
        bra     CHKMOR          ; and check the next BD
WRAP_R  move.w  RBASE2(a5),PRDPTR       
CHKMOR  move.w  PRDPTR,d0       ; put PRDPTR into d0
        cmp.w   RBPRT2(a5),d0   ; if PRDPTR=RBPRT2
        bne     REC_BD
        rts                     ; done servicing RXB/RXF
        
*** Error Handler Ignores Receive Errors
ERR_HAND        bra     CONT


**** demo data
                ORG INIT+$2000
                
RXBUF:  ds.b    MBUFS*4                 ; reserve space for 4 buffers


*put data in send buffers
TBUF0:  dc.b    STADDRL,STADDRH         ; Note that address is backwards
        dc.b    00,'Frame 1 data'
TBUF1:  dc.b    STADDRL,STADDRH
        dc.b    00,'Frame 2 data'
TBUF2:  dc.b    00,00
        dc.b    00,'Unreceived Frame' 
TBUF3:  dc.b    $ff,$ff
        dc.b    00,'Frame 4 data'                
TBUF4:                                  
*					; Dummy label
                                  
PRDPTR: ds.w    1                       ; pointer to next Receive BD to
*                                       ; process

        END


