;-------------------------------------------------------------------------------
; Dieser Datei kann kopiert und modifiziert werden, solange dieser Header in der 
; Datei belassen wird. Der Autor haftet nicht fuer Schaeden jedweder Art, die im
; Zusammenhang mit dieser Datei enstehen.
; Patrick Urban
; Lotharstr. 176
; 47057 Duisburg
; Tel.: ++49 (0)203 352287
; e-Mail: patrick.urban@freenet.de
;-------------------------------------------------------------------------------
; Kodak 1: uC-code for slideprojectors which are controlled via m-objects
;
; uC-data: 4MHz, AT90S2313
; used ports: 
; PORT B: Bit 7 : transport forward
;         Bit 6 : transport backward 
;         Bit 5 : brightness
;         Bit 4 : brightness
;         Bit 3 : brightness (in next version used as PWM-signal for TRIAC)
;         Bit 2 : brightness
;         Bit 1 : brightness
;         Bit 0 : brightness
;
; PORT D: Bit 6 : n.u.
;         Bit 5 : n.u.
;         Bit 4 : n.u.
;         Bit 3 : n.u.
;         Bit 2 : n.u.
;         Bit 1 : Tx for UART
;         Bit 0 : Rx for UART
;
; supported commands of P-COM protocoll:
; - RandomAccess (but slide is transported step by step) (Parameter Mode)
; - SetBrightness                                        (Parameter Mode)
; - GroupAddress                                         (Parameter Mode)
; - SystemReturn                                         (StatusMode)
;-------------------------------------------------------------------------------


.include "2313def.inc"

;-- Adressen im konst-Bereich:

;-- Adressen im SRAM:

.equ SRAMOffset     = 0x0060
.equ ActSlidePos    = 0x0063
.equ NewSlidePos    = 0x0064

;-- Definition verschiedener Konstanten:

.equ GlobalAdrC     = 0x1F      ; Globale Projektoradresse         ; global projector address
.equ BaudRate       = 25        ; 9600 BAUD bei 4MHz               ; baud-rate for data-transmission
.equ TraySizeC      = 80        ; number of slides in tray         ; max. number of slides in tray 
.equ BackTimeC      = 9         ; 250ns * 1024 * 256 * BackTimeC   ; time to activate a slide-transport backward
.equ ForTimeC       = 10        ; 250ns * 1024 * 256 * ForTimeC    ; time to activate a slide-transport forward
.equ TransTimeC     = 25        ; 250ns * 1024 * 256 * TransTimeC  ; time for a transport of a slide (both directions)

.equ ParameterModeC = 0x00      ; 00000 00 0 (Mode)
.equ SetResetModeC  = 0x02      ; 00000 01 0 (Mode)
.equ DirectModeC    = 0x04      ; 00000 10 0 (Mode)
.equ StatusModeC    = 0x06      ; 00000 11 0 (Mode)

.equ RandomAccessC  = 0x00      ; 0000 00X 0 (command in 'Parameter Mode')
.equ SetBrightnessC = 0x10      ; 0001 XXX 0 (command in 'Parameter Mode')
.equ GroupAddressC  = 0x30      ; 0011 000 0 (command in 'Parameter Mode')

.equ GetTrayC       = 0xA0      ;            (command in 'Status Mode')
.equ GetKeysC       = 0xB0      ;            (command in 'Status Mode')
.equ SysStatC       = 0xC0      ;            (command in 'Status Mode')
.equ SysRetC        = 0xD0      ;            (command in 'Status Mode')

;----------------------------------------------------------------------------------------------------
; hier folgen Register, die staendig verfuegbar sein sollen
;----------------------------------------------------------------------------------------------------

.def Tmp1           = r16  ; universelles Register
.def Tmp2           = r17  ; universelles Register
.def Tmp3           = r18  ; universelles Register
.def Tmp4           = r19  ; universelles Register
       
.def PrjAdr         = r3   ; Adresse des Projektores aus dem EEPROM bei Programmstart  
.def GroupAdr       = r4   ; address if projector is combined in a group (0x10-0x1E)
.def Bright         = r5   ; actual brightness
.def MovSlide       = r6   ; actual transport
.def FlagReg        = r21  ; 7: '1', wenn neues Kommando eingelaufen ist (3. Byte)
                           ; 6: '1', wenn Byte gesendet werden soll (Tx-Routine)
                           ; 5:
                           ; 4:
                           ; 3: '1', wenn Relais-Aktivierungszeit, '0', wenn Transportzeit
                           ; 2: '1', wenn ActSlidePos != NewSlidePos
                           ; 1: '1', wenn 2. Byte eingelaufen ist
                           ; 0: '1', wenn 1. Byte eingelaufen ist
.def PulseTime      = r22

;----------------------------------------------------------------------------------------------------
; hier folgen Macros
;----------------------------------------------------------------------------------------------------

.macro   PUSHTmp
         push Tmp4           ;
         push Tmp3           ;
         push Tmp2           ;
         push Tmp1           ;
.endmacro

.macro   POPTmp
         pop  Tmp1           ;
         pop  Tmp2           ;
         pop  Tmp3           ;
         pop  Tmp4           ;
.endmacro

;-------------------------------------------------------------------------------------------------------
; Interrupt Sprungziele
;-------------------------------------------------------------------------------------------------------
.cseg
.org 0
          rjmp  Reset                  ; steht immer am Anfang um zum Reset-Handling zu gelangen
.org INT0addr                          ; Externel Interrupt 0
          reti
.org INT1addr                          ; Externel Interrup 1
          reti
.org OVF0addr                          ; Wartezeit
          rjmp  TIM0_OVF
.org URXCaddr                          ; UART RX Complete Handle
          rjmp  UART_RXC    
.org UTXCaddr                          ; UART TX CompleteHandle
          rjmp  UART_TXC    
;-------------------------------------------------------------------------------------------------------
;-------------------------------------------------------------------------------------------------------
;-------------------------------------------------------------------------------------------------------

.cseg
.org 0x0100 ; das hier kann noch angepasst werden, ist aber schon ganz gut

;-------------------------------------------------------------------------------------------------------
;-- EEPROM Lese-Routine
;-------------------------------------------------------------------------------------------------------

.def     EEdrd        = r0   ; fuer die EEPROM Leseroutine
.def     EEard        = r16  ; fuer die EEPROM Leseroutine

EERead:
        sbic  EECR,EEWE
        rjmp  EERead
        out   EEAR,EEard
        sbi   EECR,EERE
        in    EEdrd,EEDR
        ret
;-@-----------------------------------------------------------------------------------------------------
;-------------------------------------------------------------------------------------------------------
;-- set stackpointer

Reset:      ldi   Tmp1,RAMEND
            out   SPL,Tmp1

;--------------------------------------------------------------------------------
;-- clear SRAM from 0060h - 018Ch (routine breaks at 0200h)

ClearSRAM:  clr   Tmp1
            ldi   Tmp2,$02
            clr   ZH
            ldi   ZL,$60
DoMore:     st    Z+,Tmp1
            cpse  ZH,Tmp2
            rjmp  DoMore

;--
;-@------------------------------------------------------------------------------
;-- initialize registers und memory

Init:       clr   FlagReg
            clr   Tmp2
            clr   Tmp3
            clr   Tmp4

            ldi   Tmp1,BaudRate        ; set UART-BAUT-register
            out   UBRR,Tmp1
            
            ldi   Tmp1,0b10010000      ; enable RX Complete Interrupt and Receiver 
            out   UCR,Tmp1
            
            ser   Tmp1                 
            out   DDRB,Tmp1            ; set all bits of PortB to output
            clr   Tmp1
            clr   MovSlide
            clr   Bright
;            com   Tmp1                 ; only for testboard @
            out   PORTB,Tmp1           ; set all Bits to '0'

            ldi   Tmp1,0b01111100
            out   DDRD,Tmp1            ; set Rx, Tx to input, all other bits are set to output
                                       ; bit 7 not available for 2313

;--------------------------------------------------------------------------------
;-- Timer0

            ldi   Tmp1,0b00000010      ; Timer/Counter0 Overflow Interrupt Flag (3-28)
            out   TIMSK,Tmp1

;--------------------------------------------------------------------------------

            sei                        ; enable Interrupts in status Register
            
;--------------------------------------------------------------------------------
;-- read Projector address from EEPROM
	
            ldi   EEard,0             ; load projector address from EEPROM
            rcall EERead
            mov   PrjAdr,EEdrd        ; and save it in register

            ldi   Tmp1,GlobalAdrC
            mov   GroupAdr,Tmp1       ; set GroupAdr to GlobalAdr

;-@-----------------------------------------------------------------------------------------------------
;-- Begin of main-programm
;-------------------------------------------------------------------------------------------------------
Main:       sbrc  FlagReg,7           ; new command ?
            rjmp  NewCommand          ; yes
            sbrc  FlagReg,2           ; actual SlidePos differs from new SlidePos in SRAM?
            rjmp  MoveTray            ; yes
            rjmp  Main                ; no, jump back to Main
            
NewCommand: cbr   FlagReg,0b10000000  ; clear 'NewCommandFlag'
            lds   Tmp1,SRAMOffset     ; load byte 1 from SRAM
            lds   Tmp2,SRAMOffset+1   ; load byte 2 from SRAM
            lds   Tmp3,SRAMOffset+2   ; load byte 3 from SRAM
            andi  Tmp1,0x06           ; mask only the command
            cpi   Tmp1,ParameterModeC 
            breq  ParaMode
            cpi   Tmp1,SetResetModeC
            breq  SetResMode          
            cpi   Tmp1,DirectModeC
            breq  DirectMode
            cpi   Tmp1,StatusModeC
            breq  StatusMode
            rjmp  Main                ; no known mode received

ParaMode:   mov   Tmp4,Tmp2
            andi  Tmp4,0b11110000     ; mask only command
            cpi   Tmp4,RandomAccessC
            breq  RandomAccess
            cpi   Tmp4,SetBrightnessC
            breq  SetBrightness
            cpi   Tmp4,GroupAddressC
            breq  GroupAddress
            rjmp  Main

RandomAccess:
            andi  Tmp2,0b00000010      ; blende das Kommando aus
            lsr   Tmp2                 ; verschiebe um 1 stelle nach rechts
            lsr   Tmp2                 ; verschiebe nochmal, dabei faellt das Bit 0 in das C-Flag
            ror   Tmp3                 ; Tmp3 muss auch noch einmal verschoben werden, um die unterste '0' wegzubekommen
                                       ; dabei wird das C-Flag nach Bit 7 geschoben
            sts   NewSlidePos,Tmp3     ; speichere nun die gewuenschte Position im SRAM
;	    sbrs  FlagReg,4            ; is process of slide-movement activ?
            sbr   FlagReg,0b00000100   ; no, set NewSlidePos in SRAM-Bit in Flag-Reg
            rjmp  Main

SetBrightness: 
            andi  Tmp2,0b00001110      ; blende das Kommando aus
            lsr   Tmp2                 ; verschiebe um 1 stelle nach rechts
            lsr   Tmp2                 ; verschiebe nochmal, dabei faellt das Bit 0 in das C-Flag
            ror   Tmp3                 ; Tmp3 muss auch noch einmal verschoben werden, um die unterste '0' wegzubekommen
                                       ; dabei wird das C-Flag nach Bit 7 geschoben
            andi  Tmp3,0xF0            ; clear lower 4 bits, only the higher 6 are important 00000065 43210000
            or    Tmp3,Tmp2            ; Tmp3 = 43210065
            swap  Tmp3                 ; Tmp3 = 00654321
            mov   Bright,Tmp3          ; save brightness
            or    Tmp3,MovSlide
;            com   Tmp3                 ; only for testboard @
            out   PORTB,Tmp3           ; set port B
            rjmp  Main                           

GroupAddress:
            lsr   Tmp3
            mov   GroupAdr,Tmp3
            rjmp  Main            

SetResMode: rjmp  Main
DirectMode: rjmp  Main
StatusMode: cpi   Tmp2,GetTrayC
            breq  GetTray
            cpi   Tmp2,GetKeysC
            breq  GetKeys
            cpi   Tmp2,SysStatC
            breq  SysStat
            cpi   Tmp2,SysRetC
            breq  SysRet
            rjmp  Main

GetTray:    rjmp  Main
GetKeys:    rjmp  Main
SysStat:    rjmp  Main
SysRet:     sbi   UCR,3              ; set TXEN-Bit
            sbi   UCR,6              ; set TXCIE-Bit
            ldi   Tmp1,0b00000110
            sbr   FlagReg,0b01000000 ; set Tx-Bit in Flag-Reg
            out   UDR,Tmp1
            sbrc  FlagReg,6
            rjmp  PC-1
            ldi   Tmp1,0b11010000
            sbr   FlagReg,0b01000000 ; set Tx-Bit in Flag-Reg
            out   UDR,Tmp1
            sbrc  FlagReg,6
            rjmp  PC-1
            ldi   Tmp1,0b10000010
            sbr   FlagReg,0b01000000 ; set Tx-Bit in Flag-Reg
            out   UDR,Tmp1
            sbrc  FlagReg,6
            rjmp  PC-1
            ldi   Tmp1,0b00110001
            sbr   FlagReg,0b01000000 ; set Tx-Bit in Flag-Reg
            out   UDR,Tmp1
            sbrc  FlagReg,6
            rjmp  PC-1
            ldi   Tmp1,0b00000000
            sbr   FlagReg,0b01000000 ; set Tx-Bit in Flag-Reg
            out   UDR,Tmp1
            sbrc  FlagReg,6
            rjmp  PC-1
            cbi   UCR,3              ; clear TXEN-Bit
            rjmp  Main

;-------------------------------------------------------------------------------------------------------

MoveTray:   cbr   FlagReg,0b00000100 ; clear bit: New-Position-not-yet-reached (bit 2 in FlagReg)
;            sbr   FlagReg,0b00010000 ; set bit 4 to show that a slide-movement is in progress
            lds   Tmp1,ActSlidePos   
            lds   Tmp2,NewSlidePos   
            mov   Tmp3,Tmp1          
            mov   Tmp4,Tmp2          
            cp    Tmp4,Tmp3          ; New - Act
            brsh  NewSOH             ; New is (S)ame (O)r (H)igher
                                     
            sub   Tmp3,Tmp4          ; Act = Act - New
            cpi   Tmp3,TraySizeC/2   ; compare with tray-size                          ; 
            brsh  SlideFor          
            rjmp  SlideBack           
                                     
NewSOH:     cp    Tmp4,Tmp3          ; compare to equal
            breq  Branch1            ; actual and new position are equal
            sub   Tmp4,Tmp3          ; New = New - Act
            cpi   Tmp4,TraySizeC/2   ; compare with tray-size                          ; 
            brsh  SlideBack           
            rjmp  SlideFor          
Branch1:    rjmp  Main               
                                     
SlideFor:   cpi   Tmp1,TraySizeC     ; Overflow from TraySizeC to 0 ?
            breq  UpperLimit         ; yes: branch to UpperLimit
            inc   Tmp1               ; no:  increment actual-slide-position
            sts   ActSlidePos,Tmp1   ; store new actual-slide-position
            rjmp  SlideForA          ;
UpperLimit: clr   Tmp1               ; set actual-slide-position to 0
            sts   ActSlidePos,Tmp1   ; store new actual-slide-position
                                     
SlideForA:  sbr   FlagReg,0b00001000 ; set Relais-Time-Bit in Flag-Reg
            ldi   PulseTime,ForTimeC ; load relais-pulse-time for a transport forward
            clr   Tmp1               ; clear Timer0-register
            out   TCNT0,Tmp1         
            ldi   Tmp1,0b00000101    ; and start Timer0 with Clk/1024
            out   TCCR0,Tmp1         
            ldi   Tmp1,0b10000000    ; activate port for transport forward
            mov   MovSlide,Tmp1     
            or    Tmp1,Bright
;            com   Tmp1               ; only for testboard @
            out   PORTB,Tmp1         
            rjmp  Main               
                                     
SlideBack:  cpi   Tmp1,0             ; Underflow from 0 to TraySizeC?
            breq  LowerLimit         ; yes: branch to LowerLimit
            dec   Tmp1               ; no:  decrement actual-slide-position
            sts   ActSlidePos,Tmp1   ; store new actual-slide-position
            rjmp  SlideBackA         ;  
LowerLimit: ldi   Tmp1,TraySizeC     ; set actual-slide-position to TraySizeC
            sts   ActSlidePos,Tmp1   ; store new actual-slide-position
                                     
SlideBackA: sbr   FlagReg,0b00001000 ; set Relais-Time-Bit in Flag-Reg
            ldi   PulseTime,BackTimeC
            clr   Tmp1               ; lade das Zaehlregister mit einer 0 vor
            out   TCNT0,Tmp1         
            ldi   Tmp1,0b00000101    ; und starte den Timer0 mit Clk/1024
            out   TCCR0,Tmp1         
            ldi   Tmp1,0b01000000    ; aktiviere das Ausgangsbit fuer den Transport rueckwaerts
            mov   MovSlide,Tmp1     
            or    Tmp1,Bright
;            com   Tmp1               ; only for testboard @
            out   PORTB,Tmp1
            rjmp  Main
            
;-------------------------------------------------------------------------------------------------------
;-- hier ist das Ende des Hauptprogramms
;-------------------------------------------------------------------------------------------------------

;-------------------------------------------------------------------------------------------------------
;-- Interrupt for Timer0-Overflow
;-------------------------------------------------------------------------------------------------------

TIM0_OVF:   ;dekrementiere SlideTime so lange bis SlideTime = 0 ist
            ;deaktiviere die Ausgangsbits fuer den Transport
            ;warte, bis die Transportzeit verstrichen ist
            ;ist sie erreicht, dann :
            ;halte den Zaehler an
            ;vergleiche, ob die neue Position schon erreicht wurde (New = Act)
            ;wenn ja, dann springe zurueck
            ;ansonsten setze das Bit NewSlidePos in SRAM im Flagregister wieder

            PUSHTmp;
            dec   PulseTime           ; 
            sbrs  FlagReg,3           ; calculate time to activate the relais or calculate time for transport?
            rjmp  TransTime           ; bit is cleared -> calculate time for transport
            cpi   PulseTime,0         ; time for relais activation already expired?
            breq  ClearRelais         ; yes: branch to ClearRelais
            POPTmp
            reti

ClearRelais: 
            clr   Tmp1                ; deactivate port-bits for transport
            clr   MovSlide     
            or    Tmp1,Bright
;            com   Tmp1               ; only for testboard @
            out   PORTB,Tmp1
            cbr   FlagReg,0b00001000  ; clear Relais-Time-Bit, jetzt ist die Transport-Zeit dran
            ldi   PulseTime,TransTimeC
            POPTmp
            reti
            
TransTime:  cpi   PulseTime,0
            breq  ClearTrans
            POPTmp
            reti            

ClearTrans: clr   Tmp1                ; stop Timer0
            out   TCCR0,Tmp1          ;
            lds   Tmp1,ActSlidePos
            lds   Tmp2,NewSlidePos
            cp    Tmp1,Tmp2           ; is NewSlidePosition reached?
            breq  PosReached          ; yes
            sbr   FlagReg,0b00000100  ; no,set bit: New-Position-not-yet-reached (bit 2 in FlagReg) 
            
PosReached: 
;	cbr   FlagReg,0b00010000  ; clear bit to show that slide-movement has finished
            POPTmp
            reti

;-------------------------------------------------------------------------------------------------------
;-- Interrupt for UART data receive
;-------------------------------------------------------------------------------------------------------

UART_RXC:   PUSHTmp;
            in    Tmp1,UDR            ; lese das Daten-Receive-Register aus
            sbrs  Tmp1,0              ; wenn bit 0 gesetzt ist, dann ist das empfangene Byte das erste Byte
            rjmp  Byte23              ; ansonsten handelt es sich um Byte 2 oder 3
            mov   Tmp2,Tmp1           ; sichere das empfangene (1te) Byte temporaer
            lsr   Tmp2                ; Adresse wird ueberprueft:
            lsr   Tmp2                ; blende nur die Adresse ein
            lsr   Tmp2                ; shifte dafuer 3 mal nach rechts
            cp    Tmp2,PrjAdr         ; vergleiche nun, ob der Projektor angesprochen wird
            breq  StoreByte1          ; ja, Adresse war eindeutig
            cpi   Tmp2,GlobalAdrC     ; compare, if global address for all projectores is usede
            breq  StoreByte1          ; yes: global address was recognized
            cp    Tmp2,GroupAdr       ; compare, if group address is used
            breq  StoreByte1          ; yes: global address was recognized
            POPTmp                    ; no: ignore byte
            reti
           
StoreByte1: sts   SRAMOffset,Tmp1     ; speichere das erste Byte
            sbr   FlagReg,0b00000001  ; setze das '1stByteReceivedFlag' im Flagregister
            POPTmp
            reti

Byte23:     sbrs  FlagReg,0           ; wurde schon das 1 Byte empfangen und wurde der Projektor angesprochen?
            rjmp  NoWrite             ; nein, dann warte auf dass naechste Byte
            sbrs  FlagReg,1           ; ja, ist aber schon das 2te Byte eingelaufen ?
            rjmp  StoreByte2
StoreByte3: sts   SRAMOffset+2,Tmp1            
            sbr   FlagReg,0b10000000  ; setze das 'CommandCompleteFlag' im Flagregister
            cbr   FlagReg,0b00000011  ; und nehme die 1st- and 2ndByteReceivedFlags wieder weg
NoWrite:    POPTmp
            reti
            
StoreByte2: sts   SRAMOffset+1,Tmp1
            sbr   FlagReg,0b00000010  ; setze das '2ndByteReceivedFlag' im Flagregister
            POPTmp
            reti
            
;-------------------------------------------------------------------------------------------------------
;-- Interrupt for UART data-transmission
;-------------------------------------------------------------------------------------------------------

UART_TXC:   cbr   FlagReg,0b01000000  ; loesche das TX-Bit wieder, wenn Byte gesendet wurde
            reti
            
;-------------------------------------------------------------------------------------------------------
;-- EEPROM: Adresse des Projektores
;-------------------------------------------------------------------------------------------------------
.eseg
.org 0x00                
        EEPrjAdr : .DB 0b00000010        ; Adresse dieses Proj ist 2
            
            
; SRAMOffset + 0 : 1. uebertragendes Byte
; SRAMOffset + 1 : 2. uebertragendes Byte
; SRAMOffset + 2 : 3. uebertragendes Byte            

; SRAMOffset + 3 : ActSlidePos : actual slide position
; SRAMOffset + 4 : NewSlidePos : new slide position


;             TIM0_OVF
;      PulseTime = PulseTime - 1
; relais-time   or   transport-time?
;      |                  |
; PulseTime = 0?     PulseTime = 0?
;   n|     y|
;  reti   
;
