; ==============================================================================
; ATmega328P -- 10m-Band CW-Bake (MO5 + 6s Pause + 20s Träger)
; Taktung CPU: Interner 8 MHz RC-Oszillator (LFUSE = 0xE2)
; Frequenz:    28,350 MHz via Si5351A (I2C an PC4/PC5, 25 MHz Referenzquarz)
; Timing:      12 WPM, 1s-Watchdog-Taktung (Power-Down Sleep Mode)
; Schutz:      Automatic Hard-Reset nach 3s ohne Hauptcode-Lebenszeichen
; ==============================================================================

.include "m328pdef.inc"

; ------------------------------------------------------------------------------
; Register-Zuordnungen
; ------------------------------------------------------------------------------
.def SECONDS   = r18          ; Zähler für Sekunden innerhalb der 60s-Minute
.def HANG_CTR  = r19          ; Zähler für unbeantwortete Sekunden (Störungsschutz)

.equ SI5351_ADDR  = 0xC0      ; I2C Schreibadresse (0x60 << 1)
.equ CLK_ENABLE   = 3         ; Register Output Enable Control

; Timing bei 12 WPM (1 Dit = 100 ms)
.equ DIT_MS       = 100
.equ DAH_MS       = 300
.equ PAUSE_ELEM   = 100
.equ PAUSE_CHAR   = 300

.cseg
.org 0x0000
    rjmp reset
.org WDTaddr                  ; Watchdog Interrupt Vektor
    rjmp wdt_isr

reset:
    ; Stackpointer initialisieren
    ldi r16, LOW(RAMEND)
    out SPL, r16
    ldi r16, HIGH(RAMEND)
    out SPH, r16

    ; I2C Hardware (TWI) initialisieren (100 kHz Takt bei 8 MHz CPU-Takt)
    ldi r16, 32               ; Angepasst für 8 MHz Systemtakt
    sts TWBR, r16
    ldi r16, (1<<TWEN)
    sts TWCR, r16

    ; Si5351A Hardware initialisieren & Frequenz auf 28,350 MHz setzen
    rcall init_si5351

    clr SECONDS               ; Sekundenzähler auf 0
    clr HANG_CTR              ; Störungszähler auf 0

    ; Watchdog-Konfiguration: Combined Mode (Interrupt + Reset nach Timeout)
    cli
    wdr
    lds r16, WDTCSR
    ori r16, (1<<WDCE) | (1<<WDE)
    sts WDTCSR, r16
    ; WDIE (Interrupt Enable) + WDE (Reset Enable), Intervall 1.0s (WDP2 | WDP1)
    ldi r16, (1<<WDIE) | (1<<WDE) | (1<<WDP2) | (1<<WDP1)
    sts WDTCSR, r16
    sei

main_loop:
    ; Hauptprogramm ist aktiv -> Störungszähler zurücksetzen ("Füttern")
    clr HANG_CTR

    ; ==========================================================================
    ; ABLAUF-STEUERUNG IM 60-SEKUNDEN-RASTER
    ; ==========================================================================

    ; --- Sekunde 0: CW-Aussendung "MO5" (~3,3s - 4,0s) ---
    cpi SECONDS, 0
    brne check_pause1
    
    rcall send_mo5
    rjmp sleep_until_wdt

check_pause1:
    ; --- Sekunde 4 bis 9 (6 Sekunden): Pause ---
    cpi SECONDS, 4
    brlo sleep_until_wdt
    cpi SECONDS, 10
    brlo is_pause1
    rjmp check_carrier

is_pause1:
    rcall transmitter_off
    rjmp sleep_until_wdt

check_carrier:
    ; --- Sekunde 10 bis 29 (20 Sekunden): Dauerstrich ---
    cpi SECONDS, 10
    brne check_carrier_end
    rcall transmitter_on      ; Bei Sekunde 10 Sender EIN
    rjmp sleep_until_wdt

check_carrier_end:
    cpi SECONDS, 30
    brne sleep_until_wdt
    rcall transmitter_off     ; Bei Sekunde 30 Sender AUS

sleep_until_wdt:
    ; Power-Down Sleep Mode aktivieren (CPU schläft bis zum Watchdog-Interrupt)
    ldi r16, (1<<SM1) | (1<<SE)
    out SMCR, r16
    sleep
    clr r16                   ; Sleep nach Aufwachen deaktivieren
    out SMCR, r16
    rjmp main_loop


; ==============================================================================
; WATCHDOG INTERRUPT SERVICE ROUTINE (ISR) - MIT 3s HARD-RESET LOGIK
; ==============================================================================
wdt_isr:
    wdr                       ; HW-Watchdog-Timer zurücksetzen

    inc SECONDS               ; Minutenzähler hochzählen
    cpi SECONDS, 60
    brne check_hang
    clr SECONDS               ; Neustart bei 0 zur vollen Minute

check_hang:
    inc HANG_CTR              ; Sekunden ohne Hauptcode-Aktivität zählen
    cpi HANG_CTR, 3           ; Sind 3 Sekunden ohne Freigabe vergangen?
    brge force_reset_mode     ; Ja -> Keinen Interrupt mehr reaktivieren, Reset erzwingen!

    ; Normalbetrieb: WDIE erneut setzen, um den Hardware-Reset abzuwenden
    lds r16, WDTCSR
    ori r16, (1<<WDIE)
    sts WDTCSR, r16
    reti

force_reset_mode:
    ; Wenn 3s lang kein `clr HANG_CTR` aufgerufen wurde: WDIE NICHT mehr aktivieren.
    ; Beim nächsten Watchdog-Takt (1s) löst die Hardware den echten HARD-RESET aus.
    reti


; ==============================================================================
; CW-SENDUNG & TELEGRAFIE-ELEMENTE
; ==============================================================================

send_mo5:
    ; --- M ( - - ) ---
    rcall send_dah
    rcall send_dah
    rcall delay_char_space
    
    ; --- O ( - - - ) ---
    rcall send_dah
    rcall send_dah
    rcall send_dah
    rcall delay_char_space
    
    ; --- 5 ( . . . . . ) ---
    rcall send_dit
    rcall send_dit
    rcall send_dit
    rcall send_dit
    rcall send_dit
    ret

send_dit:
    clr HANG_CTR              ; Störungszähler füttern
    rcall transmitter_on
    ldi r16, DIT_MS / 10
    rcall delay_10ms_units
    rcall transmitter_off
    ldi r16, PAUSE_ELEM / 10
    rcall delay_10ms_units
    ret

send_dah:
    clr HANG_CTR              ; Störungszähler füttern
    rcall transmitter_on
    ldi r16, DAH_MS / 10
    rcall delay_10ms_units
    rcall transmitter_off
    ldi r16, PAUSE_ELEM / 10
    rcall delay_10ms_units
    ret

delay_char_space:
    ldi r16, (PAUSE_CHAR - PAUSE_ELEM) / 10
    rcall delay_10ms_units
    ret


; ==============================================================================
; HARDWARE-STEUERUNG (Si5351A RF-Tastung & Initialisierung)
; ==============================================================================

transmitter_on:
    rcall i2c_start
    ldi r16, SI5351_ADDR
    rcall i2c_write
    ldi r16, CLK_ENABLE
    rcall i2c_write
    ldi r16, 0xFE             ; Output 0 (CLK0) Enable
    rcall i2c_write
    rcall i2c_stop
    ret

transmitter_off:
    rcall i2c_start
    ldi r16, SI5351_ADDR
    rcall i2c_write
    ldi r16, CLK_ENABLE
    rcall i2c_write
    ldi r16, 0xFF             ; Output 0 Disable (Stummschaltung)
    rcall i2c_write
    rcall i2c_stop
    ret

init_si5351:
    rcall transmitter_off     ; RF-Ausgang vorerst stummschalten

    ; --- 1. MultiSynth 0 & PLLA Register beschreiben (28,350 MHz @ 25 MHz Quarz) ---
    rcall i2c_start
    ldi r16, SI5351_ADDR
    rcall i2c_write
    ldi r16, 26               ; Startadresse Register 26 (MS0 Parameters)
    rcall i2c_write

    ldi r16, 0x00             ; Reg 26: MS0_P3[15:8]
    rcall i2c_write
    ldi r16, 0x01             ; Reg 27: MS0_P3[7:0]
    rcall i2c_write
    ldi r16, 0x00             ; Reg 28: MS0_P1[17:16]
    rcall i2c_write
    ldi r16, 0x07             ; Reg 29: MS0_P1[15:8]
    rcall i2c_write
    ldi r16, 0x1E             ; Reg 30: MS0_P1[7:0]
    rcall i2c_write
    ldi r16, 0x00             ; Reg 31: MS0_P3/P2 Format
    rcall i2c_write
    ldi r16, 0x00             ; Reg 32: MS0_P2[15:8]
    rcall i2c_write
    ldi r16, 0x00             ; Reg 33: MS0_P2[7:0]
    rcall i2c_write
    rcall i2c_stop

    ; --- 2. CLK0 an PLLA koppeln (Register 16) ---
    rcall i2c_start
    ldi r16, SI5351_ADDR
    rcall i2c_write
    ldi r16, 16               ; Register CLK0_CTRL
    rcall i2c_write
    ldi r16, 0x4F             ; PLLA, Fractional Mode, 8mA Drive
    rcall i2c_write
    rcall i2c_stop

    ; --- 3. PLLA Reset durchführen (Register 177) ---
    rcall i2c_start
    ldi r16, SI5351_ADDR
    rcall i2c_write
    ldi r16, 177              ; Register PLL Reset
    rcall i2c_write
    ldi r16, 0xA0             ; Reset PLLA & PLLB
    rcall i2c_write
    rcall i2c_stop
    ret


; ==============================================================================
; I2C (TWI) & SOFTWARE-DELAYS
; ==============================================================================

i2c_start:
    ldi r16, (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)
    sts TWCR, r16
wait_i2c1:
    lds r16, TWCR
    sbrs r16, TWINT
    rjmp wait_i2c1
    ret

i2c_write:
    sts TWDR, r16
    ldi r16, (1<<TWINT)|(1<<TWEN)
    sts TWCR, r16
wait_i2c2:
    lds r16, TWCR
    sbrs r16, TWINT
    rjmp wait_i2c2
    ret

i2c_stop:
    ldi r16, (1<<TWINT)|(1<<TWSTO)|(1<<TWEN)
    sts TWCR, r16
    ret

delay_10ms_units:
    push r16
    push r17
    push r18
d10_loop:
    ldi r17, 104              ; Für 8 MHz Systemtakt halbiert (vorher 208)
d10_inner1:
    ldi r18, 255
d10_inner2:
    dec r18
    brne d10_inner2
    dec r17
    brne d10_inner1
    dec r16
    brne d10_loop
    pop r18
    pop r17
    pop r16
    ret
