;-------------------------------------------------------------------------------
; VLF Kalender-Morse-Empfaenger mit Trägem Peak-Hold S-Meter (vlf_rx.asm)
; Dekodiert VLF bei 8.3333 kHz und misst die Signalstaerke ueber ADC0 (Pin 23)
; S-Meter haelt den Spitzenwert fuer 2s und faellt danach sehr langsam ab.
; Ausgabe: Zeile 1 = Text, Zeile 2 = [■■■■■      ] + Signal
; Taktfrequenz des ATmega328P: 1 MHz (Werkseinstellung)
;-------------------------------------------------------------------------------
.include "m328pdef.inc"

;--- Registerdefinitionen ---
.def temp         = r16
.def lcd_data     = r17
.def sample_cnt   = r18     ; Zaehlt die Signal-Praesenz (Timing)
.def morse_len    = r19     ; Anzahl der Elemente im aktuellen Zeichen
.def morse_pat    = r20     ; Bit-Muster (0=Dit, 1=Dah)
.def space_cnt    = r21     ; Zaehlt die Dauer der Signalstille
.def adc_val      = r22     ; Aktueller ADC-Wert (ADCH)
.def bar_cnt      = r23     ; Hilfsregister fuer die Balkengrafik
.def adc_max      = r24     ; Gespeicherter Spitzenwert fuer das S-Meter
.def decay_cnt    = r25     ; Zaehlt die Hold-Zeit und bremst den Abfall

; Warteschleifen-Register (unterer Block)
.def dcnt1        = r2     
.def dcnt2        = r3
.def dcnt3        = r4

;--- LCD-Pin-Definitionen ---
.equ LCD_PORT     = PORTD
.equ LCD_DDR      = DDRD
.equ LCD_CTRL_PT  = PORTB
.equ LCD_CTRL_DR  = DDRB
.equ LCD_RS       = 1       ; PB1
.equ LCD_E        = 2       ; PB2

;-------------------------------------------------------------------------------
; Interrupt-Vektortabelle
;-------------------------------------------------------------------------------
.org 0x0000
    rjmp RESET
.org OC1Aaddr
    rjmp TIMER1_COMPA_ISR   ; Sampling-Takt & ADC-Start: Alle 10ms

;-------------------------------------------------------------------------------
; Initialisierung
;-------------------------------------------------------------------------------
RESET:
    ; Stackpointer initialisieren
    ldi temp, low(RAMEND)
    out SPL, temp
    ldi temp, high(RAMEND)
    out SPH, temp

    ; LCD-Steuerleitungen als Ausgang
    sbi LCD_CTRL_DR, LCD_RS
    sbi LCD_CTRL_DR, LCD_E
    cbi LCD_CTRL_PT, LCD_RS
    cbi LCD_CTRL_PT, LCD_E

    ; LCD-Datenleitungen (obere 4 Bits von PORTD) als Ausgang
    ldi temp, 0xF0
    out LCD_DDR, temp

    ; Digitaler Signaleingang: PD2 (INT0-Pin) vom MAX9010 kommend
    cbi DDRD, 2
    sbi PORTD, 2

    ; --- ADC Setup ---
    ; REFS0 = AVCC, ADLAR = 1 (Linksbuendig für 8-Bit Aufloesung in ADCH)
    ; MUX3..0 = 0000 fuer ADC0 (Pin 23)
    ldi temp, (1<<REFS0) | (1<<ADLAR)
    sts ADMUX, temp
    ; ADEN = ADC an, ADPS1..0 = Prescaler 4 (1MHz / 4 = 250kHz ADC-Takt)
    ldi temp, (1<<ADEN) | (1<<ADPS1)
    sts ADCSRA, temp

    ; Variablen loeschen
    clr sample_cnt
    clr space_cnt
    clr morse_len
    clr adc_max
    clr decay_cnt
    ldi morse_pat, 0x01

    ; LCD initialisieren
    rcall LCD_INIT

    ; Statischen Text der ersten Zeile schreiben
    ldi lcd_data, 'V'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'L'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'F'
    rcall LCD_DATA_OUT
    ldi lcd_data, ' '
    rcall LCD_DATA_OUT
    ldi lcd_data, 'S'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'I'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'G'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'N'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'A'
    rcall LCD_DATA_OUT
    ldi lcd_data, 'L'
    rcall LCD_DATA_OUT
    ldi lcd_data, ':'
    rcall LCD_DATA_OUT

    ; Cursor auf Zeile 2, Position 0 setzen fuer S-Meter
    rcall REFRESH_BAR_CURSOR

    ; --- Timer 1 Setup: 10ms Sampling-Intervall ---
    ldi temp, high(156)     ; 1 MHz / 64 / 100 Hz = 156
    sts OCR1AH, temp
    ldi temp, low(156)
    sts OCR1AL, temp
    ldi temp, 0x00
    sts TCCR1A, temp
    ldi temp, (1<<WGM12) | (1<<CS11) | (1<<CS10) ; CTC, Prescaler 64
    sts TCCR1B, temp

    ldi temp, (1<<OCIE1A)   ; Interrupt aktivieren
    sts TIMSK1, temp

    sei                     ; Interrupts global erlauben

MAIN_LOOP:
    rjmp MAIN_LOOP

;-------------------------------------------------------------------------------
; ISR Timer 1: 10ms Takt für Morse-Tastung und Feldstaerkemessung
;-------------------------------------------------------------------------------
TIMER1_COMPA_ISR:
    ; 1. ADC-Messung starten
    lds temp, ADCSRA
    ori temp, (1<<ADSC)
    sts ADCSRA, temp

WAIT_ADC:
    lds temp, ADCSRA
    sbrc temp, ADSC
    rjmp WAIT_ADC
    lds adc_val, ADCH       ; Hole den aktuellen 8-Bit Wert (0-255)

    ; --- Traege Peak-Hold-Logik ---
    cp adc_val, adc_max     ; Vergleiche neuen Wert mit bisherigem Maximum
    brcc NEW_PEAK           ; Wenn neuer Wert groesser oder gleich -> neuer Peak

    ; Kein neuer Peak: Pruefe ob Hold-Zeit (2 Sekunden) abgelaufen ist
    tst decay_cnt
    breq DECAY_PROCESS      ; Wenn 0, springe zum langsamen Absinken
    dec decay_cnt           ; Sonst Hold-Zeit runterzaehlen
    rjmp UPDATE_DISPLAY

NEW_PEAK:
    mov adc_max, adc_val    ; Aktualisiere den Spitzenwert
    ldi decay_cnt, 200      ; Setze Hold-Zeit auf 200 Ticks (200 * 10ms = 2 Sekunden)
    rjmp UPDATE_DISPLAY

DECAY_PROCESS:
    ; Um den Abfall ueber einen groesseren Zeitraum zu strecken, nutzen wir
    ; decay_cnt negativ als Teiler. Wir lassen adc_max nur alle 4 Ticks sinken.
    dec decay_cnt           ; Wird von 0 auf 255, 254, 253... heruntergezaehlt
    mov temp, decay_cnt
    andi temp, 0x03         ; Nur alle 4 Ticks (40ms) wahr
    brne UPDATE_DISPLAY

    tst adc_max
    breq UPDATE_DISPLAY     ; Bereits bei 0, kein weiterer Abfall noetig
    dec adc_max             ; Spitzenwert ganz langsam verringern

UPDATE_DISPLAY:
    ; S-Meter Balken mit dem gedaempften adc_max aktualisieren
    rcall UPDATE_SMETER

    ; 2. Digitalen Morse-Kanal abtasten (PD2)
    sbis PIND, 2
    rjmp SIGNAL_PRESENT

    ; --- SIGNALSTILLE ---
    tst sample_cnt
    breq NO_CARRIER_END
    
    rcall EVALUATE_IMPULSE  ; Impuls auswerten (Dit/Dah)
    clr sample_cnt

NO_CARRIER_END:
    inc space_cnt
    cpi space_cnt, 35       ; > 350ms Pause? -> Zeichen fertig
    brcc DECODE_CHARACTER
    reti

SIGNAL_PRESENT:
    ; --- TRÄGER VORHANDEN ---
    clr space_cnt
    inc sample_cnt
    reti

;-------------------------------------------------------------------------------
; S-Meter Balkengrafik generieren (Zeile 2, Spalte 0 bis 7)
;-------------------------------------------------------------------------------
UPDATE_SMETER:
    push lcd_data
    rcall REFRESH_BAR_CURSOR ; Cursor auf Anfang Zeile 2 setzen

    ; Wir teilen adc_max (0-255) durch 32, um max 8 Balken-Segmente zu erhalten
    mov temp, adc_max
    lsr temp
    lsr temp
    lsr temp
    lsr temp
    lsr temp                ; temp = adc_max / 32 (Wert von 0 bis 8)
    mov bar_cnt, temp

draw_blocks:
    tst bar_cnt
    breq fill_spaces
    ldi lcd_data, 0xFF      ; Voller HD44780-Blockcharakter
    rcall LCD_DATA_OUT
    dec bar_cnt
    rjmp draw_blocks

fill_spaces:
    mov bar_cnt, temp
    ldi temp, 8
    sub temp, bar_cnt
    mov bar_cnt, temp
draw_spaces:
    tst bar_cnt
    breq smeter_done
    ldi lcd_data, ' '
    rcall LCD_DATA_OUT
    dec bar_cnt
    rjmp draw_spaces

smeter_done:
    ldi lcd_data, '|'
    rcall LCD_DATA_OUT
    pop lcd_data
    ret

REFRESH_BAR_CURSOR:
    ldi lcd_data, 0xC0      ; Zeile 2, Spalte 0
    rcall LCD_CMD_OUT
    ret

;-------------------------------------------------------------------------------
; Morse-Dekodierung & Zeichentabelle
;-------------------------------------------------------------------------------
DECODE_CHARACTER:
    cpi morse_len, 0
    breq DECODE_SKIP

    ; Setze den Cursor hinter das S-Meter (Zeile 2, Spalte 10) vor der Ausgabe
    ldi lcd_data, 0xCA      
    rcall LCD_CMD_OUT

    cpi morse_len, 1
    breq LEN_1
    cpi morse_len, 2
    breq LEN_2
    cpi morse_len, 3
    breq LEN_3
    cpi morse_len, 4
    breq LEN_4
    cpi morse_len, 5
    breq LEN_5
    rjmp RESET_DECODER

LEN_1:
    cpi morse_pat, 0b00000010
    breq CHAR_E
    cpi morse_pat, 0b00000011
    breq CHAR_T
    rjmp RESET_DECODER
LEN_2:
    cpi morse_pat, 0b00000110
    breq CHAR_M
    cpi morse_pat, 0b00000100
    breq CHAR_I
    rjmp RESET_DECODER
LEN_3:
    cpi morse_pat, 0b00001111
    breq CHAR_O
    cpi morse_pat, 0b00001100
    breq CHAR_D
    cpi morse_pat, 0b00001000
    breq CHAR_S
    rjmp RESET_DECODER
LEN_4:
    cpi morse_pat, 0b0011011
    breq CHAR_Y
    cpi morse_pat, 0b0010000
    breq CHAR_H
    rjmp RESET_DECODER
LEN_5:
    cpi morse_pat, 0b00111111
    breq CHAR_1
    cpi morse_pat, 0b00111110
    breq CHAR_2
    cpi morse_pat, 0b00111100
    breq CHAR_3
    cpi morse_pat, 0b00111000
    breq CHAR_4
    cpi morse_pat, 0b00110000
    breq CHAR_5
    cpi morse_pat, 0b00100000
    breq CHAR_6
    cpi morse_pat, 0b00100001
    breq CHAR_7
    cpi morse_pat, 0b00100011
    breq CHAR_8
    cpi morse_pat, 0b00100111
    breq CHAR_9
    cpi morse_pat, 0b00101111
    breq CHAR_0

RESET_DECODER:
    clr morse_len
    ldi morse_pat, 0x01
DECODE_SKIP:
    reti

CHAR_E:
    ldi lcd_data, 'E'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_T:
    ldi lcd_data, 'T'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_M:
    ldi lcd_data, 'M'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_I:
    ldi lcd_data, 'I'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_O:
    ldi lcd_data, 'O'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_D:
    ldi lcd_data, 'D'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_S:
    ldi lcd_data, 'S'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_Y:
    ldi lcd_data, 'Y'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_H:
    ldi lcd_data, 'H'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_1:
    ldi lcd_data, '1'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_2:
    ldi lcd_data, '2'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_3:
    ldi lcd_data, '3'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_4:
    ldi lcd_data, '4'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_5:
    ldi lcd_data, '5'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_6:
    ldi lcd_data, '6'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_7:
    ldi lcd_data, '7'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_8:
    ldi lcd_data, '8'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_9:
    ldi lcd_data, '9'
    rcall PRINT
    rjmp RESET_DECODER
CHAR_0:
    ldi lcd_data, '0'
    rcall PRINT
    rjmp RESET_DECODER

PRINT:
    rcall LCD_DATA_OUT
    ret

EVALUATE_IMPULSE:
    cpi sample_cnt, 20
    brcc IS_DAH
    lsl morse_pat
    rjmp IMPULSE_DONE
IS_DAH:
    lsl morse_pat
    ori morse_pat, 0x01
IMPULSE_DONE:
    inc morse_len
    ret

;-------------------------------------------------------------------------------
; HD44780 LCD-Treiber (4-Bit Modus)
;-------------------------------------------------------------------------------
LCD_INIT:
    rcall delay_100ms       
    ldi temp, 0x30
    out LCD_PORT, temp
    rcall LCD_TOGGLE_E
    rcall delay_5ms
    rcall LCD_TOGGLE_E
    rcall delay_100ms
    rcall LCD_TOGGLE_E
    ldi temp, 0x20          
    out LCD_PORT, temp
    rcall LCD_TOGGLE_E
    ldi lcd_data, 0x28      
    rcall LCD_CMD_OUT
    ldi lcd_data, 0x0C      
    rcall LCD_CMD_OUT
    ldi lcd_data, 0x01      
    rcall LCD_CMD_OUT
    rcall delay_5ms
    ret

LCD_CMD_OUT:
    cbi LCD_CTRL_PT, LCD_RS 
    rcall LCD_SEND_NIBBLES
    ret

LCD_DATA_OUT:
    sbi LCD_CTRL_PT, LCD_RS 
    rcall LCD_SEND_NIBBLES
    ret

LCD_SEND_NIBBLES:
    mov temp, lcd_data
    andi temp, 0xF0
    out LCD_PORT, temp
    rcall LCD_TOGGLE_E
    mov temp, lcd_data
    swap temp
    andi temp, 0xF0
    out LCD_PORT, temp
    rcall LCD_TOGGLE_E
    rcall delay_5ms
    ret

LCD_TOGGLE_E:
    sbi LCD_CTRL_PT, LCD_E
    nop
    nop
    cbi LCD_CTRL_PT, LCD_E
    ret

delay_5ms:
    ldi  temp, 70
    mov  dcnt2, temp
W2: ldi  temp, 70
    mov  dcnt3, temp
W3: dec  dcnt3
    brne W3
    dec  dcnt2
    brne W2
    ret

delay_100ms:
    ldi  temp, 2
    mov  dcnt1, temp
    ldi  temp, 4
    mov  dcnt2, temp
    ldi  temp, 186
    mov  dcnt3, temp
W1: dec  dcnt3
    brne W1
    dec  dcnt2
    brne W1
    dec  dcnt1
    brne W1
    ret
