/*******************************************************************************
 * $B%o%s%3%$%s%G%3!<%@#6!J%]%$%s%HMQ%=%U%H%&%'%"!K(B - Ver 2.0 - for PIC12F1822
 * Copyright (C) 2007-2026  Nucky
 * $B;HMQ(BPIC$B%^%$%3%s(B PIC12F1822$B!!!!FbItH/?6(B32MHz$B%b!<%I(B
 *  
 * $B"(<V:\%G%3!<%@$N%O!<%I%&%'%"$r%]%$%s%HMQ$H$7$F;HMQ$9$k$?$a$N%=%U%H$G$9!#(B
 * 
 * 2026.03.21 Ver2.0  $B%=!<%9%3!<%I$r(BC$B8@8l5-=R$KJQ99!J5!G=6/2=!K(B
 * 
 * $B!Z%G%3!<%@$N35MW![(B
 * DCC$B%3%^%s%I%9%F!<%7%g%s$+$i$N%"%/%;%5%j!<%Q%1%C%H$r<u?.$7!"(B
 * $B%b!<%?!<%I%i%$%P(B(TB67H450$BEy(B)$B$r2p$7$F%]%$%s%H%^%7%s$r6nF0$7$^$9!#(B
 * 
 * $B!Z<g$JFCD'![(B
 * 1. $B%3%s%Q%$%iHs0MB8(B: Timer1$B$K$h$k<B;~4V7WB,$G!"(BXC8(Free/PRO)$B$N:GE,2=:9$r5[<}!#(B
 * 2. $B40A4(BC$B8@8l2=(B: $B%$%s%i%$%s%"%;%s%V%i$rGS=|$7!"J]<i@-$H0\?"@-$r6K8B$^$G8~>e!#(B
 * 3. 32MHz$B9bB.6nF0(B: $BM>M5$N$"$k=hM}B.EY$G%Q%1%C%H2r@O$H%O!<%I%&%'%"@)8f$rN>N)!#(B
 * 4. $B7xO4$J<u?.%m%8%C%/(B: $B%N%$%:$d?.9fES@d;~$N%?%$%`%"%&%HI|5"=hM}$r40Hw!#(B
 * 
 * $B!Z%T%sG[CV(B (PIC12F1822)$B![(B
 * 1 (VDD): 5V$BEE8;(B (+)
 * 2 (RA5): ECCP P1A / $B%b!<%?!<%I%i%$%P7PM3%]%$%s%H%^%7%s$X(B
 * 3 (RA4): ECCP P1B / $B%b!<%?!<%I%i%$%P7PM3%]%$%s%H%^%7%s$X(B
 * 4 (RA3): MCLR ($BL$;HMQ!&F~NO@lMQ(B)
 * 5 (RA2): DCC$B?.9fF~NO(B
 * 6 (RA1): $B3+DLI=<((BLED$B=PNO(B $B"(MWDq93@\B3(B
 * 7 (RA0): $B3+DLI=<((BLED$B=PNO(B $B"(MWDq93@\B3(B
 * 8 (VSS): GND (-)
 *******************************************************************************/

/*
 *$B!Z%=!<%9%3!<%I$N%i%$%;%s%9$*$h$SMxMQ>r7o$K$D$$$F![(B
 * $BK\%=!<%9%3!<%I$NCx:n8"$O$9$Y$F(BNucky$B$K5"B0$7$^$9!#(B
 * $BK\%=%U%H%&%'%"$O!"0J2<$N>r7o$K4p$E$$$FDs6!$5$l$^$9!#(B
 *
 * 1. [$B8D?M!&Hs1DMxMxMQ(B]
 * $B8D?M$NE4F;LO7?$NHO0O$G$NMxMQ!"8&5f!"2~JQ$O<+M3$+$DL5=~$G9T$($^$9!#(B
 * $BK\%3!<%I!"$^$?$O2~JQ$7$?%3!<%I$r8x3+!&6&M-$9$k$3$H$b2DG=$G$9$,!"(B
 * $B$=$N:]$bI,$:Hs1DMx$G$N8x3+$H$7!"K\%i%$%;%s%9I=5-$r;D$7$F$/$@$5$$!#(B
 *
 * 2. [$B>&MQMxMQ!JHNGdEy!K(B]
 * $BK\%=%U%H%&%'%"!J2~JQ$7$?$b$N$r4^$`!K$r=q$-9~$s$@%^%$%3%s$d%G%3!<%@4pHD$NHNGd!"(B
 * $B@=IJ2=!"$=$NB>$$$+$J$k7A$G$"$C$F$bMx1W$rF@$kL\E*!J>&MQ!K$GMxMQ$9$k>l9g$O!"(B
 * $BI,$:;vA0$K$4O"Mm$/$@$5$$!#JLES!"M-=~$N>&MQ%i%$%;%s%9$r$47@Ls$$$?$@$-$^$9!#(B
 *
 * --- English Notice ---
 * Copyright (c) Nucky. All rights reserved.
 * 
 * 1. Personal/Non-Commercial Use: Free to use, modify, and share strictly 
 *    for personal, non-commercial hobby purposes.
 * 2. Commercial Use: ANY commercial use, including manufacturing, selling 
 *    products, or integrating this software into items for sale, is STRICTLY 
 *    PROHIBITED without prior permission. A paid commercial license is required. 
 *    Please contact the author via the website.
 *
 * --- $BLH@U;v9`(B / Disclaimer ---
 * $BK\%W%m%0%i%`$NMxMQ$K$h$C$F@8$8$?%G%3!<%@!"<VN>!"$=$NB>$N5!:`$NGKB;!"IT6q9g!"(B
 * $B$*$h$S$$$+$J$kB;32$K$D$$$F$b!":n<T$O0l@Z$N@UG$$rIi$$$+$M$^$9!#(B
 * This software is provided "AS IS", WITHOUT ANY WARRANTY. Use at your own risk.
*/


#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

// =========================================================
// 1. $BDj5A(B: $B%/%m%C%/<~GH?t$H%b!<%?!<%I%i%$%P;EMM(B
// =========================================================

// $BFbItH/?6%/%m%C%/$r(B32MHz$B$K@_Dj(B ($BCY1d4X?t(B __delay_ms() $BEy$N;~4V7W;;MQ(B)
#define _XTAL_FREQ 32000000 

// $BEk:\$9$k%b!<%?!<%I%i%$%P(BIC$B$K9g$o$;$F(BPWM$B$N6K@-(B($B@5O@M}(B/$BIiO@M}(B)$B$r@Z$jBX$($^$9!#(B
#define BD6231      // BD6231F-E2 $BEy(B ($B0lHLE*$J@)8fMQ(B)
//#define TB67H450  // TB67H450FNG $BEy(B (Slow Decay$B6nF0$,I,MW$J%I%i%$%PMQ(B)

#if defined(BD6231)
    // BD6231: PWM$BHs=PNOB&%T%s$r(B LOW(0V) $B$K8GDj!#(BPWM$B$O@5O@M}(B(Active-High)$B$G6nF0!#(B
    #define MOTOR_FIXED_LVL 0
    #define PWM_MODE_VAL    0x0C 
#elif defined(TB67H450)
    // TB67H450: PWM$BHs=PNOB&%T%s$r(B HIGH(5V) $B$K8GDj!#(BPWM$B$OIiO@M}(B(Active-Low)$B$G6nF0!#(B
    #define MOTOR_FIXED_LVL 1
    #define PWM_MODE_VAL    0x0F 
#else
    #define MOTOR_FIXED_LVL 0
    #define PWM_MODE_VAL    0x0C 
#endif

// =========================================================
// 2. $B%3%s%U%#%0%l!<%7%g%s@_Dj(B ($B%^%$%3%s$N4pK\%O!<%I%&%'%"@_Dj(B)
// =========================================================
#pragma config FOSC = INTOSC    // $BFbItH/?64o$r;HMQ(B ($B%T%s@aLs(B)
#pragma config WDTE = OFF       // $B%&%)%C%A%I%C%0%?%$%^L58z(B (Timer1$B$N%?%$%`%"%&%H$GBeMQ(B)
#pragma config PWRTE = ON       // $B%Q%o!<%"%C%W%?%$%^M-8z(B ($B5/F0;~$NEE050BDjBT$A(B)
#pragma config MCLRE = OFF      // MCLR$B%T%sL58z(B (RA3$B$r%G%8%?%kF~NO2=(B)
#pragma config CP = OFF         // $B%W%m%0%i%`%a%b%jJ]8nL58z(B
#pragma config CPD = OFF        // $B%G!<%?%a%b%j(B(EEPROM)$BJ]8nL58z(B
#pragma config BOREN = ON       // $B%V%i%&%s%"%&%H%j%;%C%HM-8z(B ($BEE05Dc2<;~$NK=AvKI;_(B)
#pragma config CLKOUTEN = OFF   // $B%/%m%C%/=PNOL58z(B
#pragma config IESO = OFF       // $BFbIt(B/$B30It%/%m%C%/@ZBXL58z(B
#pragma config FCMEN = OFF      // $B%U%'%$%k%;!<%U%/%m%C%/%b%K%?L58z(B
#pragma config WRT = OFF        // $B%U%i%C%7%e<+8J=q$-9~$_J]8nL58z(B
#pragma config PLLEN = ON       // 4$BG\(BPLL$BM-8z(B ($BFbIt(B8MHz$B$r(B4$BG\$N(B32MHz$B$K>:05$7$F9bB.F0:n(B)
#pragma config STVREN = ON      // $B%9%?%C%/%*!<%P!<%U%m!<%j%;%C%HM-8z(B
#pragma config BORV = HI        // $B%V%i%&%s%"%&%HEE05$r9b$a(B($BLs(B2.7V)$B$K@_Dj$7FMF~EEN.BP:v(B
#pragma config LVP = OFF        // $BDcEE05%W%m%0%i%_%s%0L58z(B (ON$B$@$H(BRA3$B$,;H$($J$$(B)

// =========================================================
// 3. EEPROM CV$B%"%I%l%9Dj5A(B $B$H(B $B=i4|CM@_Dj(B
// =========================================================
// $B%W%m%0%i%`Fb$GFI$_=q$-$9$k(BCV$B$N(BEEPROM$BFb%"%I%l%9(B
#define EEPROM_CV1   0x00 // CV1: $B%"%I%l%92<0L(B
#define EEPROM_CV2   0x01 // CV2: $B%"%I%l%9>e0L(B
#define EEPROM_CV3   0x02 // CV3: $B=PNO:nF0;~4V(B ($B@_DjCM(B $B!_(B 10ms)
#define EEPROM_CV4   0x03 // CV4: $BF0:n%b!<%I(B (0:$B%b!<%?!<<0O"B3=PNO(B, 1:$B%=%l%N%$%I<0%Q%k%9=PNO(B)
#define EEPROM_CV5   0x04 // CV5: $B=PNOCY1d;~4V(B ($B@_DjCM(B $B!_(B 10ms)
#define EEPROM_CV6   0x05 // CV6: PWM Duty$BHf(B (0$B!A(B255)
#define EEPROM_CV8   0x07 // CV8: $B%a!<%+!<(BID(156)$B!#!V(B8$B!W=q$-9~$_$G%j%;%C%HH/F0(B
#define EEPROM_DIR_STATE 0x08 // $BEE8;(BOFF$BD>A0$N!V%]%$%s%H3+DLJ}8~!W$r5-21$9$k@lMQ%"%I%l%9(B

// $B%W%m%0%i%`=q$-9~$_;~$N%G%U%)%k%H(BCV$BCM(B (1$B9T:GBg(B8$B%P%$%H(B)
// [CV1]=129, [CV2]=248 ($B%"%I%l%9(B1), [CV3]=5(50ms), [CV4]=1($B%=%l%N%$%I(B), [CV5]=1(10ms), [CV6]=127($BLs(B50%), [CV7]=96(Ver0.96), [CV8]=156
__EEPROM_DATA(129, 248,   5,   1,   1, 127,  96, 156); 
// $B%"%I%l%9(B0x08$B!A(B0x0F (EEPROM_DIR_STATE $BMQ$N=i4|CM(B)
__EEPROM_DATA(  0,   0,   0,   0,   0,   0,   0,   0); 

// =========================================================
// 4. $B%0%m!<%P%kJQ?t(B ($B>uBV4IM}!&%G!<%?%-%c%C%7%e(B)
// =========================================================

// --- $BF0:n%U%i%09=B$BN(B ---
// $B%S%C%H%U%#!<%k%I$rMQ$$$F%a%b%j$r@aLs$7!"%Q%1%C%H2r@O$H<B9T%m%8%C%/$r66EO$7$7$^$9!#(B
typedef struct {
    unsigned data_rcv : 1; // $B<+?H$X$NE>49L?Na$r<u?.$7$?%U%i%0(B (1:$B<u?.$"$j(B, 0:$BBT5!(B)
    unsigned dir      : 1; // $BE>49L\I8$NJ}8~(B (0:$BD>?J(B, 1:$B6J@~(B)
} PointFlag;
PointFlag p_flag;

// --- CV$BCM%-%c%C%7%e(B ---
// EEPROM$B$+$i$NFI$_=P$7$O=hM};~4V$,$+$+$k$?$a!"5/F0;~$K(BRAM$B$X%3%T!<$79bB.;2>H$7$^$9!#(B
uint8_t cv1_c, cv2_c, cv3_c, cv4_c, cv5_c, cv6_c;
uint8_t current_point_dir = 0; // $B8=:_$N%]%$%s%H3+DLJ}8~(B (LED$BI=<($N99?7$d(BEEPROM$B<wL?J]8n$K;HMQ(B)
uint8_t page_register = 0;     // Page$B%b!<%I%W%m%0%i%_%s%0;~$N%Z!<%85-21MQ(B

// --- DCC$B%Q%1%C%H<u?.%P%C%U%!(B ---
// C$B8@8l2=$K$h$j%"%;%s%V%iFCM-$N%P%s%/@)Ls$,>CLG$7$?$?$a!"%3%s%Q%$%i$KG[CV$rG$$;$^$9!#(B
uint8_t dcc_data[6] = {0}; // $B<u?.$7$?@8$N%Q%1%C%H%G!<%?(B($B:GBg(B6$B%P%$%H(B)
uint8_t dcc_byte_cnt = 0;  // $B<u?.$7$?%Q%1%C%H$NM-8z$J%P%$%H?t(B
uint8_t packet_ready = 0;  // 1=$B%Q%1%C%H<u?.40N;!#%a%$%s%k!<%W$G$N2r@O%H%j%,!<(B

// =========================================================
// $B4X?t%W%m%H%?%$%W@k8@(B
// =========================================================
void Init_System(void);
void Load_CV_Cache(void);
void Update_LED_Display(void);
void Process_Operation_Mode(void);
void Process_Service_Mode(void);
void Execute_Turnout(uint8_t target_dir);
void Output_Control(void);
void Send_Ack(void);
void Write_CV_With_Reset(uint8_t cv_addr, uint8_t cv_data);

// =========================================================
// $B!Z4X?t![(B DCC$B%Q%k%9I}7WB,(B (Timer1 $B%O!<%I%&%'%"7WB,(B)
// C$B8@8l$N%k!<%W(B(while)$B$K$h$k%=%U%H%&%'%"7WB,$G$O$J$/!"%^%$%3%sFbB"$N(B
// $B%O!<%I%&%'%"%?%$%^!<(B(Timer1)$B$rMQ$$$F!"(BDCC$B%Q%k%9$NJ*M}E*$J;~4V(B($B&L(Bs)$B$r<BB,$7$^$9!#(B
// $B$3$l$K$h$j(BXC8$B$N(BFree$BHG(B/PRO$BHG$K$h$k:GE,2=$N0c$$$r40A4$K5[<}$7$^$9!#(B
// $BLa$jCM(B: 1 ($BC;$$(B=DCC$B$N(B'1'), 0 ($BD9$$(B=DCC$B$N(B'0'), 2 ($B%?%$%`%"%&%H(B=$B%(%i!<(B)
// =========================================================
uint8_t Measure_Pulse(void) {
    // 1. RA2$B$,(BLow$B$K$J$k$N$rBT$D(B ($B%Q%k%9$NN)$A>e$,$j%(%C%8$rBT5!(B)
    TMR1H = 0; TMR1L = 0; // $B%?%$%^!<%j%;%C%H(B
    while(PORTAbits.RA2 == 1) { 
        // Init_System$B$G@_Dj$7$?DL$j!"(B1$B%+%&%s%H!a(B1$B&L(Bs$B$G2C;;$5$l$^$9!#(B
        // TMR1H$B$,(B1$B$K$J$k(B($B$D$^$j(B256$B&L(Bs$B7P2a$9$k(B)$B$H0[>o$H$_$J$7!"%(%i!<C&=P$7$^$9!#(B
        if (TMR1H > 0) return 2; 
    }
    
    // 2. RA2$B$,(BHigh$B$K$J$k(B($B%Q%k%9$N3+;O(B)$B$N$rBT$D(B
    TMR1H = 0; TMR1L = 0;
    while(PORTAbits.RA2 == 0) { 
        if (TMR1H > 0) return 2; 
    }
    
    // 3. $B%Q%k%9$ND9$5(B(High$B$N4|4V(B)$B$r(BTimer1$B$G<BB,$9$k(B
    TMR1H = 0; TMR1L = 0;
    while(PORTAbits.RA2 == 1) { 
        if (TMR1H > 0) return 2; 
    }
    
    // 4. $B%Q%k%9I}$NH=Dj(B
    // DCC$B5,3J(B: '1'$B$OJRGHLs(B58$B&L(Bs$B!"(B'0'$B$OLs(B100$B&L(Bs$B!#Cf4V$N(B 75$B&L(Bs $B$rogCM$H$7$^$9!#(B
    uint8_t pulse = TMR1L;
    if (pulse < 75) return 1;
    return 0;
}

// =========================================================
// $B!Z4X?t![(B DCC$B%Q%1%C%H<u?.=hM}(B ($B>uBVA+0\%k!<%W(B)
// $B%W%j%"%s%V%k$rC5$7!":GBg(B6$B%P%$%H$N%G!<%?$r%S%C%HC10L$GAH$_N)$F$^$9!#(B
// =========================================================
void Process_DCC_Packet(void) {
    packet_ready = 0;
    uint8_t bit_val;
    uint8_t buf[6] = {0}; 
    uint8_t p_count = 0;
    
WAIT_PREAMBLE:
    p_count = 0;
    
PREAMBLE_LOOP:
    // --- 1. $B%W%j%"%s%V%k8!=P(B (14$B8D0J>e$N(B '1' $B%S%C%H$NO"B3(B) ---
    bit_val = Measure_Pulse();
    if (bit_val == 2) return; // $B%?%$%`%"%&%H;~$O%a%$%s%k!<%W$XB(;~I|5"(B
    
    if (bit_val == 1) {
        p_count++; // '1'$B$J$i%+%&%s%H%"%C%W(B
        goto PREAMBLE_LOOP;
    } else {
        // '0'$B$,Mh$?;~!"D>A0$K(B'1'$B$,(B10$B2s0J>eB3$$$F$$$J$1$l$P%N%$%:$HH=CG$7C5$7D>$9(B
        if (p_count < 10) goto WAIT_PREAMBLE; 
    }
    
    // --- 2. $B%G!<%?%P%$%H$N<u?.(B ($B:GBg(B6$B%P%$%H(B) ---
    for (dcc_byte_cnt = 0; dcc_byte_cnt < 6; dcc_byte_cnt++) {
        
        // 8$B%S%C%HJ,$N%G!<%?$rFI$_9~$_!":8%7%U%H$7$F(B1$B%P%$%H$KAH$_N)$F$k(B
        for (uint8_t i = 0; i < 8; i++) {
            bit_val = Measure_Pulse();
            if (bit_val == 2) return;
            buf[dcc_byte_cnt] = (buf[dcc_byte_cnt] << 1) | bit_val;
        }
        
        // 8$B%S%C%HD>8e$N!V%;%Q%l!<%?%S%C%H!W$r3NG'(B
        bit_val = Measure_Pulse();
        if (bit_val == 2) return;
        
        // --- 3. $B%Q%1%C%H%(%s%IH=Dj(B ---
        if (bit_val == 1) {
            // $B%;%Q%l!<%?$,(B '1' $B$J$i%Q%1%C%H=*N;!#:GDc(B3$B%P%$%HB7$C$F$$$l$PM-8z$H$9$k!#(B
            dcc_byte_cnt++; 
            if (dcc_byte_cnt >= 3) {
                dcc_data[0] = buf[0]; dcc_data[1] = buf[1]; dcc_data[2] = buf[2];
                dcc_data[3] = buf[3]; dcc_data[4] = buf[4]; dcc_data[5] = buf[5];
                packet_ready = 1; 
            }
            return;
        }
        // $B%;%Q%l!<%?$,(B '0' $B$J$i!"$^$@<!$N%P%$%H$,B3$/$N$G%k!<%W$r7QB3$9$k(B
    }
}

// ==========================================================
// $B!Z%a%$%s4X?t![(B $B%W%m%0%i%`$N?4B!It(B
// ==========================================================
void main(void) {
    Init_System();       // $B%O!<%I%&%'%"$N=i4|@_Dj(B
    Load_CV_Cache();     // EEPROM$B$+$i(BRAM$B$X(BCV$BCM$r%m!<%I(B
    Update_LED_Display();// $B5/F0D>8e!"A02s5-21$7$F$$$?J}8~$N(BLED$B$rE@Et(B

    p_flag.data_rcv = 0; 

    // $BL58B%k!<%W(B
    while(1) {
        // 1. DCC$B%Q%1%C%H$N<u?.BT5!(B ($B%?%$%`%"%&%H$G>o$K2s$k(B)
        Process_DCC_Packet(); 
        
        // 2. $B<u?.40N;;~$N%Q%1%C%H2r@O(B
        if (packet_ready) {
            uint8_t xor_sum = 0;
            // XOR$B%A%'%C%/%5%`$K$h$k%G!<%?2=$18!>Z(B
            for (uint8_t i = 0; i < dcc_byte_cnt; i++) {
                xor_sum ^= dcc_data[i];
            }
            
            if (xor_sum == 0 && dcc_byte_cnt >= 3) {
                // $BBh(B1$B%P%$%H$,(B 0111 xxxx (0x70 $B!A(B 0x7F) $B$G;O$^$k>l9g$O%5!<%S%9%b!<%I(B($B%W%m%0%i%_%s%0(B)
                if ((dcc_data[0] & 0xF0) == 0x70) {
                    Process_Service_Mode();
                }
                // $B$=$l0J30(B(0x80 $B!A(B 0xBF)$B$O%*%Z%l!<%7%g%s%b!<%I(B($B%]%$%s%HE>49(B)
                else {
                    Process_Operation_Mode();
                }
            }
        }
        
        // 3. $B%]%$%s%HE>49$N<B9T4IM}(B ($B%U%i%0$,N)$C$F$$$l$P6nF04X?t$r8F$V(B)
        Output_Control();
    }
}

// ==========================================================
// $B!Z4X?t![(B $B%O!<%I%&%'%"=i4|2=(B
// $B%^%$%3%s5/F0;~$K(B1$BEY$@$18F$P$l!"%T%s$NLr3d$d%?%$%^!<$r@_Dj$7$^$9!#(B
// ==========================================================
void Init_System(void) {
    OSCCON = 0xF0;      // $BFbIt%/%m%C%/(B 32MHz (8MHz$B%Y!<%9(B x 4$BG\(BPLL)
    ANSELA = 0x00;      // $BA4%T%s$r%G%8%?%k(BI/O$B$K@_Dj(B
    TRISA  = 0x0C;      // RA2(DCC$BF~NO(B), RA3(MCLR$BF~NO(B)$B$O!VF~NO!W!"B>$O!V=PNO!W(B
    LATA   = 0x00;      // $B=PNO%T%s=i4|2=(B

    APFCON = 0x03;      // ECCP$B$N(BPWM$B=PNO$r(B P1A->RA5, P1B->RA4 $B$K%k!<%F%#%s%0(B

    // --- PWM(Timer2) $B$N@_Dj(B ---
    // PR2=255$B@_Dj;~!"(BPWM$B<~GH?t$OLs(B31.25kHz$B$H$J$j$^$9!#(B
    // CV6$B$N(B0$B!A(B255$B$rD>@\(BDuty$BHf%l%8%9%?(B(CCPR1L)$B$KBeF~$G$-$k$?$a7W;;$,ITMW$G$9!#(B
    PR2 = 255;          
    T2CON = 0x04;       // Timer2 ON, $B%W%j%9%1!<%i(B1:1
    CCP1CON = PWM_MODE_VAL; 
    CCPR1L = 0;         // $B=i4|(BDuty 0% ($BDd;_(B)
    PSTR1CON = 0x00;    // $B%9%F%"%j%s%0(B($B=PNO@h$N;XDj(B)$B$rA4$FL58z2=$70BA43NJ](B

    // --- DCC$B%Q%k%97WB,MQ(B(Timer1) $B$N@_Dj(B ---
    // 32MHz$B6nF0;~!"FbIt%/%m%C%/8;(B(Fosc/4)$B$O(B 8MHz(0.125$B&L(Bs)$B!#(B
    // $B%W%j%9%1!<%i$r(B1:8$B$K@_Dj$9$k$3$H$G!"(B8MHz / 8 = 1MHz $B$H$J$j!"(B
    // $B!V(BTimer1$B$N(B1$B%+%&%s%H(B $B!a(B $B%T%C%?%j(B1$B&L(Bs$B!W$H$$$&Hs>o$K07$$$d$9$$;~4V<4$r:n$j$^$9!#(B
    T1CONbits.TMR1CS = 0; 
    T1CONbits.T1CKPS = 3; 
    T1CONbits.TMR1ON = 1; 
}

// ==========================================================
// $B!Z4X?t![(B CV$B%-%c%C%7%e!&>uBV$NFI$_9~$_(B
// ==========================================================
void Load_CV_Cache(void) {
    cv1_c = eeprom_read(EEPROM_CV1);
    cv2_c = eeprom_read(EEPROM_CV2);
    cv3_c = eeprom_read(EEPROM_CV3);
    cv4_c = eeprom_read(EEPROM_CV4);
    cv5_c = eeprom_read(EEPROM_CV5);
    cv6_c = eeprom_read(EEPROM_CV6);
    
    // $BEE8;(BOFF$BA0$K5-21$7$F$$$?!V3+DLJ}8~!W$rI|85(B
    current_point_dir = eeprom_read(EEPROM_DIR_STATE);
    
    // EEPROM$B$,L$=q$-9~$_(B(0xFF)$B$@$C$?>l9g$N0BA4=hM}(B
    if (current_point_dir == 0xFF) {
        current_point_dir = 0; 
        eeprom_write(EEPROM_DIR_STATE, current_point_dir);
    }
}

// ==========================================================
// $B!Z4X?t![(B LED$BI=<(99?7(B
// ==========================================================
void Update_LED_Display(void) {
    if (current_point_dir == 0) {
        LATA0 = 1; LATA1 = 0; // $BD>?J(B(Close) : RA0$B$rE@Et(B
    } else {
        LATA0 = 0; LATA1 = 1; // $B6J@~(B(Throw) : RA1$B$rE@Et(B
    }
}

// ==========================================================
// $B!Z4X?t![(B CV$B=q$-9~$_E}9g=hM}(B ($B9)>l=P2Y%j%;%C%HBP1~(B)
// ==========================================================
void Write_CV_With_Reset(uint8_t cv_addr, uint8_t cv_data) {
    // CV8$B$K!V(B8$B!W$,=q$-9~$^$l$?$i!"<gMW$J(BCV$B$r%G%U%)%k%HCM$KLa$9(B
    if (cv_addr == EEPROM_CV8 && cv_data == 8) {
        eeprom_write(EEPROM_CV1, 129); eeprom_write(EEPROM_CV2, 248);
        eeprom_write(EEPROM_CV3, 5);   eeprom_write(EEPROM_CV4, 1);
        eeprom_write(EEPROM_CV5, 1);   eeprom_write(EEPROM_CV6, 127);
    } else {
        eeprom_write(cv_addr, cv_data);
    }
    Load_CV_Cache(); // $B%-%c%C%7%e$r:G?72=(B
}

// ==========================================================
// $B!Z4X?t![(B $B%*%Z%l!<%7%g%s%b!<%I(B($B%]%$%s%HE>49%Q%1%C%H(B)$B2r@O(B
// ==========================================================
void Process_Operation_Mode(void) {
    if ((dcc_data[0] & 0xC0) == 0x80) {
        uint8_t addr1 = dcc_data[0];
        // $BBh(B2$B%P%$%H$N:G2<0L%S%C%H(B($BJ}8~@)8f>pJs(B)$B$rL5;k$7$F%"%I%l%9Hf3S(B
        uint8_t addr2 = dcc_data[1] & 0xFE; 
        
        if ((addr1 == cv1_c) && (addr2 == cv2_c)) {
            p_flag.dir = dcc_data[1] & 0x01; // $BE>49L\I8$NJ}8~$rCj=P(B
            p_flag.data_rcv = 1;             // $B<B9T%U%i%0$rN)$F$k(B
        }
    }
}

// ==========================================================
// $B!Z4X?t![(B $B=PNO@)8f$N%a%$%s4IM}(B
// ==========================================================
void Output_Control(void) {
    if (p_flag.data_rcv) {
        // $BF0:nCf(B($BL$3NDj>uBV(B)$B$NI=8=$H$7$F!"$^$:N>J}$N(BLED$B$r>CEt(B
        LATA0 = 0; 
        LATA1 = 0;
        
        Execute_Turnout(p_flag.dir); // $BE>49%7!<%1%s%9<B9T(B
        
        p_flag.data_rcv = 0; // $B<B9T40N;$H$7$F%U%i%0$r2<$m$9(B
    }
}

// ==========================================================
// $B!Z4X?t![(B $B%]%$%s%HE>49%7!<%1%s%9$N<B9T(B
// ==========================================================
void Execute_Turnout(uint8_t target_dir) {
    // 1. $B=PNOCY1d=hM}(B (CV5) : $BJ#?t%]%$%s%H$N0l@FF0:n$K$h$k2aEEN.$r$:$i$9(B
    for (uint8_t i = 0; i < cv5_c; i++) __delay_ms(10);

    // 2. $B4SDLEEN.KI;_(B (Dead-time) : H$B%V%j%C%82sO)$N%7%g!<%HGKB;$rKI$0(B
    PSTR1CON = 0x00; 
    LATA &= ~(0x30); // RA4, RA5 $B$r6/@)(BLOW
    CCPR1L = 0;      
    __delay_us(100);

    // 3. $B%k!<%F%#%s%0@ZBX(B : $BE>49J}8~$K1~$8$F=PNO%T%s$r@Z$jBX$($k(B
    if (target_dir == 1) {
        LATA4 = MOTOR_FIXED_LVL; 
        PSTR1CON = 0x01; // RA5(P1A)$B$X(BPWM$B%k!<%F%#%s%0(B
    } else {
        LATA5 = MOTOR_FIXED_LVL; 
        PSTR1CON = 0x02; // RA4(P1B)$B$X(BPWM$B%k!<%F%#%s%0(B
    }

    // 4. $B%]%$%s%H6nF03+;O(B : CV6(Duty)$B$G0BA4$JEE05$r=PNO(B
    CCPR1L = cv6_c; 

    // 5. $BF0:n;~4V$NBT5!(B (CV3)
    for (uint8_t i = 0; i < cv3_c; i++) __delay_ms(10);

    // 6. $B%Q%k%9Dd;_=hM}(B : $B%=%l%N%$%I<0(B(CV4=1)$B$J$i%3%$%k>FB;$rKI$0$?$a3N<B$K=PNO$r(BOFF$B$K$9$k(B
    if (cv4_c & 0x01) {
        PSTR1CON = 0x00;
        LATA &= ~(0x30);
        CCPR1L = 0;
    }

    // 7. $B>uBV99?7$H(BEEPROM$B<wL?J]8n(B : $B<B:]$KJ}8~$,JQ$o$C$?;~$@$1J]B8(B
    if (current_point_dir != target_dir) {
        current_point_dir = target_dir;
        eeprom_write(EEPROM_DIR_STATE, current_point_dir);
    }
    
    // 8. $BF0:n40N;$H$7$F?7$7$$3+DLJ}8~$N(BLED$B$rE@Et(B
    Update_LED_Display();
}

// ==========================================================
// $B!Z4X?t![(B ACK($B1~Ez(B)$BAw?.(B
// ==========================================================
void Send_Ack(void) {
    uint8_t backup_lata = LATA; 
    uint8_t backup_duty = CCPR1L;
    uint8_t backup_pstr = PSTR1CON;

    LATA4 = MOTOR_FIXED_LVL; 
    LATA |= 0x03;            // LED$BE@Et$G6O$+$G$bEEN.$r2T$0(B
    PSTR1CON = 0x01;         // RA5$B$X=PNO(B
    CCPR1L = 255;            // $B%U%k%Q%o!<(B(100%)$B$G%b!<%?$r6nF0$7EEN.$r>CHq$9$k(B

    __delay_ms(6);           // NMRA$B5,3J$GDj$a$i$l$?1~Ez;~4V(B ($BLs(B6ms)

    // $B%P%C%/%"%C%W$+$i85$N>uBV$rI|85(B
    CCPR1L = backup_duty;
    PSTR1CON = backup_pstr;
    LATA = backup_lata; 
}

// ==========================================================
// $B!Z4X?t![(B $B%5!<%S%9%b!<%I(B (CV$B%W%m%0%i%_%s%0=hM}(B)
// $B$"$i$f$k%3%^%s%I%9%F!<%7%g%s$G$NFI=q$KBP1~$9$k$?$a!"(B
// Page Mode $B$*$h$S(B Direct Mode (Byte / Bit) $B$NA4$F$rLVMe$7$^$9!#(B
// ==========================================================
void Process_Service_Mode(void) {
    uint8_t instruction = dcc_data[0]; 
    
    // --- $B!Z(B1$B![(BPAGE $B%b!<%I(B (3$B%P%$%H(B) ---
    if (dcc_byte_cnt == 3) {
        uint8_t is_write = (instruction & 0x08) ? 1 : 0; // Bit3: 1=Write, 0=Verify
        uint8_t command  = instruction & 0x07;           
        uint8_t target_data = dcc_data[1];
        uint8_t cv_addr = 0;

        if (command <= 3) { 
            // $B%G!<%?%l%8%9%?(B(R1$B!A(BR4)$B%"%/%;%9(B
            if (page_register == 0) return;
            cv_addr = (page_register - 1) * 4 + command; 
        } 
        else if (command == 0x05) { 
            // $B%Z!<%8%l%8%9%?$X$N=q$-9~$_(B
            if (is_write) page_register = target_data;
            if (!is_write && page_register != target_data) return;
            Send_Ack(); return;
        }
        else { 
            // $BD>@\%"%/%;%9(B
            cv_addr = command; 
        }

        if (is_write) {
            Send_Ack(); // $B%?%$%`%"%&%H$rKI$0$?$a!"(BEEPROM$B=q$-9~$_A0$K(BACK$B$rJV$9(B
            Write_CV_With_Reset(cv_addr, target_data);
        } else {
            if (eeprom_read(cv_addr) == target_data) Send_Ack();
        }
    }
    // --- $B!Z(B2$B![(BDIRECT $B%b!<%I(B (Byte$BC10LA`:n(B) (4$B%P%$%H(B) ---
    else if (dcc_byte_cnt == 4 && (instruction & 0xF4) == 0x74) {
        uint8_t is_write = (instruction & 0x08) ? 1 : 0; 
        uint8_t cv_addr = dcc_data[1];     
        uint8_t target_data = dcc_data[2]; 

        if (is_write) {
            Send_Ack();
            Write_CV_With_Reset(cv_addr, target_data);
        } else {
            if (eeprom_read(cv_addr) == target_data) Send_Ack();
        }
    }
    // --- $B!Z(B3$B![(BDIRECT $B%b!<%I(B (Bit$BC10LA`:n(B) (4$B%P%$%H(B) ---
    else if (dcc_byte_cnt == 4 && (instruction & 0xFC) == 0x78) {
        uint8_t cv_addr = dcc_data[1]; 
        uint8_t k_bit = (dcc_data[2] & 0x10) ? 1 : 0; // 1=Write, 0=Verify
        uint8_t d_bit = (dcc_data[2] & 0x08) ? 1 : 0; // $BL\I8$N%S%C%HCM(B
        uint8_t bit_pos = dcc_data[2] & 0x07;         // $B%S%C%H0LCV(B
        
        uint8_t current_cv_val = eeprom_read(cv_addr);
        uint8_t bit_mask = 1 << bit_pos;
        uint8_t actual_bit = (current_cv_val & bit_mask) ? 1 : 0;

        if (k_bit == 0) {
            // Verify
            if (actual_bit == d_bit) Send_Ack();
        } else {
            // Write
            Send_Ack(); 
            if (d_bit) current_cv_val |= bit_mask;  
            else       current_cv_val &= ~bit_mask; 
            
            Write_CV_With_Reset(cv_addr, current_cv_val);
        }
    }
}