/*******************************************************************************
 * $B%]%$%s%HMQ%"%/%;%5%j%G%3!<%@(B (FPM Decoder4) - Ver 2.0 - for PIC12F1828
 * Copyright (C) 2007-2026  Nucky
 * $B;HMQ(BPIC$B%^%$%3%s(B PIC12F1828$B!!!!FbItH/?6(B32MHz$B%b!<%I(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
 * PIC16F1828 (32MHz Internal Oscillator) $B$r;HMQ$7$?!"(B
 * 4$B2sO)BP1~!&>:055!G=FbB"$N%]%$%s%HMQ%"%/%;%5%j%G%3!<%@(B (FPM Decoder4)
 * $B%"%;%s%V%i$r40A4$KGS=|$7!"2DFI@-$HJ]<i@-$r6K8B$^$G9b$a$?(B100% C$B8@8l;EMM$G$9!#(B
 *
 * $B!Z<g$JEk:\5!G=$H0BA4BP:v![(B
 * 1. $B>:05%A%g%C%Q2sO)(B: KATO/TOMIX$BEy$NBgEENO%3%$%k$r3N<B$KE>49(B(CV9$B$G:GBg(B24.0V)$B!#(B
 * 2. $B%R%9%F%j%7%9@)8f(B: $BBT5!;~$NHy:Y$JEE05JQF0$K$h$k(BLED$B$N9bB.E@LG(B($B%A%i$D$-(B)$B$rKI;_!#(B
 * 3. $B%?%$%`%"%&%HJ]8n(B: Timer1$B$rMxMQ$7$?@53N$J%Q%k%97WB,$K$h$j!"?.9fES@d;~$b(B
 * $BL58B%k!<%W$K4Y$i$:!"B(:B$KC&=P$7$F>:05@)8f$r0];}$7$^$9!#(B
 * 4. EEPROM$B<wL?J]8n(B: $BF10lJ}8~$X$NO"B3L?Na;~!"L5BL$J=q$-9~$_$r%9%-%C%W!#(B
 * 5. $B%W%m%0%i%_%s%040A4BP1~(B: Page$B%b!<%I!"(BDirect$B%b!<%I(B(Byte/Bit)$B$N$9$Y$F$KBP1~!#(B
 * 6. Prog-Silence$B5!9=(B: CV$BFI$_=q$-;~!">:05(BPWM$B$r6/@)Dd;_$7$F%/%j!<%s$J(BACK$B1~Ez$r<B8=!#(B
 *
 * $B!Z%T%sG[CV(B (PIC16F1828 - 20$B%T%s(B)$B![(B
 * $B%]%$%s%H%G%3!<%@$H$7$F4pHD$K<BAu$9$k:]$N!"%^%$%3%s$N3F%T%s$NLr3d$H@\B3@h$G$9!#(B
 *
 * 1 (VDD): 5V$BEE8;(B (+)
 * 2 (RA5): DCC$B?.9fF~NO(B
 * 3 (RA4): $B>:05EE054F;kMQ(B $B%"%J%m%0F~NO(B (AN3 / $B%3%s%G%s%5EE05$NJ,05Dq93$+$i(B)
 * 4 (RA3): MCLR ($BL$;HMQ!&F~NO@lMQ(B)
 * 5 (RC5): $B>:05%A%g%C%Q6nF0MQ(B PWM$B=PNO(B (CCP1 / MOSFET$B$N%2!<%H$X(B)
 * 6 (RC4): $B2sO)(BD $B=PNO(B1 ($B%]%$%s%H%^%7%s6nF0MQ(B H$B%V%j%C%82sO)$X(B)
 * 7 (RC3): $B2sO)(BC $B=PNO(B1 ($BF1>e(B)
 * 8 (RC6): $B2sO)(BA $BJ}8~I=<((BLED$B=PNO(B
 * 9 (RC7): $B2sO)(BB $BJ}8~I=<((BLED$B=PNO(B
 * 10 (RB7): $B2sO)(BC $BJ}8~I=<((BLED$B=PNO(B
 * 11 (RB6): $B2sO)(BD $BJ}8~I=<((BLED$B=PNO(B
 * 12 (RB5): $B2sO)(BA $B=PNO(B2 ($B%]%$%s%H%^%7%s6nF0MQ(B H$B%V%j%C%82sO)$X(B)
 * 13 (RB4): $B2sO)(BB $B=PNO(B2 ($BF1>e(B)
 * 14 (RC2): $B2sO)(BC $B=PNO(B2 ($BF1>e(B)
 * 15 (RC1): $B2sO)(BB $B=PNO(B1 ($BF1>e(B)
 * 16 (RC0): $B2sO)(BA $B=PNO(B1 ($BF1>e(B)
 * 17 (RA2): $B2sO)(BD $B=PNO(B2 ($BF1>e(B)
 * 18 (RA1): $BL$;HMQ(B
 * 19 (RA0): $B>:05(B($B%A%c!<%8(B)$B40N;I=<((BLED$B=PNO(B
 * 20 (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.
*/
 
/*
 * DCC Decoder Firmware
 * * Copyright (c) Nucky. All rights reserved.
 *
 * This software is dual-licensed:
 *
 * 1. Open Source Use (GPL):
 * You may use, copy, modify, and distribute this software under the terms
 * of the GNU General Public License (GPL) for personal and open-source projects.
 *
 * 2. Commercial Use:
 * For commercial use (e.g., closed-source productization, sales, or embedding 
 * into commercial boards), a separate commercial license is required. 
 * Please contact the author prior to any commercial use.
 *
 * DISCLAIMER:
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. THE AUTHOR 
 * SHALL NOT BE LIABLE FOR ANY DAMAGE TO DECODERS, MODEL TRAINS, OR OTHER 
 * EQUIPMENT RESULTING FROM THE USE OF THIS SOFTWARE. USE AT YOUR OWN RISK.
 */


#include <xc.h>
#include <stdint.h>
#include <stdbool.h>

// =========================================================
// 1. $B%3%s%U%#%0%l!<%7%g%s@_Dj(B ($B%O!<%I%&%'%"$N4pK\F0:n@_Dj(B)
// =========================================================
#pragma config FOSC = INTOSC    // $BFbItH/?64o$r;HMQ(B ($B30IU$1%/%m%C%/ITMW(B)
#pragma config WDTE = OFF       // $B%&%)%C%A%I%C%0%?%$%^L58z(B
#pragma config PWRTE = OFF      // $B%Q%o!<%"%C%W%?%$%^L58z(B
#pragma config MCLRE = OFF      // MCLR$B%T%s(B(RA3)$B$r30It%j%;%C%H$G$O$J$/%G%8%?%kF~NO$H$7$F;HMQ(B
#pragma config CP = OFF         // $B%W%m%0%i%`%a%b%j$N%3!<%I%W%m%F%/%HL58z(B
#pragma config CPD = OFF        // $B%G!<%?%a%b%j(B(EEPROM)$B$N%3!<%I%W%m%F%/%HL58z(B
#pragma config BOREN = ON       // $B%V%i%&%s%"%&%H%j%;%C%HM-8z(B ($BEE05Dc2<;~$K0BA4$K%^%$%3%s$rDd;_(B)
#pragma config CLKOUTEN = OFF   // $B%/%m%C%/=PNOL58z(B (I/O$B%T%s$H$7$F;HMQ(B)
#pragma config IESO = OFF       // $BFbIt(B/$B30It%*%7%l!<%?@Z$jBX$(5/F0L58z(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%a%b%j=q$-9~$_J]8nL58z(B
#pragma config PLLEN = ON       // 4xPLL$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!<(B/$B%"%s%@!<%U%m!<;~$N%j%;%C%HM-8z(B
#pragma config BORV = LO        // $B%V%i%&%s%"%&%H%j%;%C%H$NEE05ogCM$rDc$a(B(Low)$B$K@_Dj(B
#pragma config LVP = ON         // $BDcEE05%W%m%0%i%_%s%0M-8z(B ($BDL>o$N=q$-9~$_4D6-MQ(B)

#define _XTAL_FREQ 32000000     // $BF0:n<~GH?t(B32MHz$B$NDj5A(B (__delay_ms$BEy$N;~4V7W;;$K;HMQ(B)

// =========================================================
// 2. $BF~=PNO%T%s$N3d$jEv$F(B ($B%^%/%mDj5A(B)
// =========================================================
// DCC$B%Q%1%C%HF~NO(B
#define DCC_IN      PORTAbits.RA5   // $B%l!<%k$+$i$N(BDCC$B?.9fF~NO%T%s(B ($B%U%)%H%+%W%iEy7PM3(B)

// $B%]%$%s%H%^%7%sE>49=PNO(B ($B%G%#%9%/%j!<%H(BH$B%V%j%C%86nF0MQ(B)
// OUT1$B$H(BOUT2$B$rGSB>E*$K(BHigh/Low$B$K$9$k$3$H$G!"%3%$%k$K@55U$NEEN.$rN.$9(B
#define A_OUT1      LATCbits.LATC0  // $B2sO)(BA $B=PNO(B1
#define A_OUT2      LATBbits.LATB5  // $B2sO)(BA $B=PNO(B2
#define B_OUT1      LATCbits.LATC1  // $B2sO)(BB $B=PNO(B1
#define B_OUT2      LATBbits.LATB4  // $B2sO)(BB $B=PNO(B2
#define C_OUT1      LATCbits.LATC3  // $B2sO)(BC $B=PNO(B1
#define C_OUT2      LATCbits.LATC2  // $B2sO)(BC $B=PNO(B2
#define D_OUT1      LATCbits.LATC4  // $B2sO)(BD $B=PNO(B1
#define D_OUT2      LATAbits.LATA2  // $B2sO)(BD $B=PNO(B2

// $B>uBVI=<((BLED$B=PNO(B
#define A_LEDOUT    LATCbits.LATC6  // $B2sO)(BA $B$N%]%$%s%H3+DLJ}8~$r<($9(BLED
#define B_LEDOUT    LATCbits.LATC7  // $B2sO)(BB $B$N%]%$%s%H3+DLJ}8~$r<($9(BLED
#define C_LEDOUT    LATBbits.LATB7  // $B2sO)(BC $B$N%]%$%s%H3+DLJ}8~$r<($9(BLED
#define D_LEDOUT    LATBbits.LATB6  // $B2sO)(BD $B$N%]%$%s%H3+DLJ}8~$r<($9(BLED
#define CHARGE_LED  LATAbits.LATA0  // $B>:05%3%s%G%s%5$N=<EE40N;$r<($9(BLED ($BE@Et(B=$B40N;(B/$BE>492D(B, $B>CEt(B=$B=<EECf(B)

// =========================================================
// 3. EEPROM $B%"%I%l%9Dj5A(B $B$H(B CV$B=i4|CM$N@_Dj(B
// =========================================================
// $B%W%m%0%i%`Fb$GFI$_=q$-$9$k(BCV$B$N(BEEPROM$BFb%"%I%l%9(B (CV$BHV9f(B - 1 = $B%"%I%l%9(B)
#define EE_CV1_ADDR  0   // CV1: $B2sO)(BA $B%"%/%;%5%j%"%I%l%9(B ($B2<0L%P%$%H(B)
#define EE_CV2_ADDR  1   // CV2: $B2sO)(BA $B%"%/%;%5%j%"%I%l%9(B ($B>e0L%P%$%H(B)
#define EE_CV3_ADDR  2   // CV3: $B=PNO:nF0;~4V(B (1$B$G(B1ms$B!#=i4|CM(B125=125ms)
#define EE_CV5_ADDR  4   // CV5: $B2sO)(BB $B%"%/%;%5%j%"%I%l%9(B ($B2<0L%P%$%H(B)
#define EE_CV6_ADDR  5   // CV6: $B2sO)(BB $B%"%/%;%5%j%"%I%l%9(B ($B>e0L%P%$%H(B)
#define EE_CV8_ADDR  7   // CV8: $B%a!<%+!<(BID(156)$B!#!V(B8$B!W=q$-9~$_$GA4(BCV$B$r9)>l=P2Y>uBV$K%j%;%C%H(B
#define EE_CV9_ADDR  8   // CV9: $B>:05L\I8EE05(B ($BCM!_(B0.1V$B!#=i4|CM(B200=20.0V / $B:GBg(B240)
#define EE_CV10_ADDR 9   // CV10: $B2sO)(BC $B%"%/%;%5%j%"%I%l%9(B ($B2<0L%P%$%H(B)
#define EE_CV11_ADDR 10  // CV11: $B2sO)(BC $B%"%/%;%5%j%"%I%l%9(B ($B>e0L%P%$%H(B)
#define EE_CV12_ADDR 11  // CV12: $B2sO)(BD $B%"%/%;%5%j%"%I%l%9(B ($B2<0L%P%$%H(B)
#define EE_CV13_ADDR 12  // CV13: $B2sO)(BD $B%"%/%;%5%j%"%I%l%9(B ($B>e0L%P%$%H(B)
#define EE_CV29_ADDR 28  // CV29: $B%3%s%U%#%.%e%l!<%7%g%s%l%8%9%?(B ($BK\5!$G$O5!G=L$;HMQ$@$,FI=q1~EzMQ$KDj5A(B)

// $BA02s$N%]%$%s%HE>49J}8~(B(LED$B>uBV(B)$B$r5-21$7$F$*$/(BEEPROM$B%"%I%l%9(B ($BEE8;:FEjF~;~$NI|85MQ(B)
#define EE_LED_A_ADDR 0xF0
#define EE_LED_B_ADDR 0xF1
#define EE_LED_C_ADDR 0xF2
#define EE_LED_D_ADDR 0xF3

// ---------------------------------------------------------
// EEPROM $B=i4|CM=q$-9~$_(B (__EEPROM_DATA$B%^%/%m(B)
// $B%^%$%3%s$K%W%m%0%i%`$r=q$-9~$s$@D>8e$K@_Dj$5$l$k%G%U%)%k%H$N(BCV$BCM$G$9!#(B
// ---------------------------------------------------------
// $B%"%I%l%9(B 0x00 ~ 0x07 (CV1 ~ CV8$B$KAjEv(B)
__EEPROM_DATA(
    129, // [Addr 0] CV1: 129 ($B2sO)(BA $B%"%I%l%92<0L(B)
    248, // [Addr 1] CV2: 248 ($B2sO)(BA $B%"%I%l%9>e0L(B)
    125, // [Addr 2] CV3: 125 ($B=PNO:nF0;~4V(B 125ms)
    0,   // [Addr 3] CV4: 0   ($BL$;HMQ(B)
    129, // [Addr 4] CV5: 129 ($B2sO)(BB $B%"%I%l%92<0L(B)
    250, // [Addr 5] CV6: 250 ($B2sO)(BB $B%"%I%l%9>e0L(B)
    20,  // [Addr 6] CV7: 30  ($B%P!<%8%g%s%J%s%P!<(B Ver 2.0 / $B40A4(BC$B8@8lHG(B)
    156  // [Addr 7] CV8: 156 ($B%a!<%+!<(BID: WebNucky)
);

// $B%"%I%l%9(B 0x08 ~ 0x0F (CV9 ~ CV16$B$KAjEv(B)
__EEPROM_DATA(
    200, // [Addr 8] CV9:  200 ($B>:05L\I8EE05(B 20.0V)
    129, // [Addr 9] CV10: 129 ($B2sO)(BC $B%"%I%l%92<0L(B)
    252, // [Addr10] CV11: 252 ($B2sO)(BC $B%"%I%l%9>e0L(B)
    129, // [Addr11] CV12: 129 ($B2sO)(BD $B%"%I%l%92<0L(B)
    254, // [Addr12] CV13: 254 ($B2sO)(BD $B%"%I%l%9>e0L(B)
    0,   // [Addr13] CV14: 0   ($BL$;HMQ(B)
    0,   // [Addr14] CV15: 0   ($BL$;HMQ(B)
    0    // [Addr15] CV16: 0   ($BL$;HMQ(B)
);

// =========================================================
// 4. $B%0%m!<%P%kJQ?t(B ($B>uBV4IM}!&%-%c%C%7%e!&%Q%1%C%H%P%C%U%!(B)
// =========================================================

// --- $BF0:n%U%i%09=B$BN(B ---
// $B%a%b%j$r@aLs$9$k$?$a!"(B1$B%S%C%HC10L$G%U%i%0$r4IM}$7$^$9!#(B
typedef struct {
    unsigned t_cmd    : 1;  // 't' (Turnout / $BJ,4t(B) $BE>49L?Na$r<u?.$7$?%U%i%0(B
    unsigned c_cmd    : 1;  // 'c' (Closed / $BD>?J(B) $BE>49L?Na$r<u?.$7$?%U%i%0(B
    unsigned data_rcv : 1;  // $B<+J,08$F$NL?Na$r<u?.$7!"<B9T$rBT$C$F$$$k>uBV$N%U%i%0(B
} PortFlag;
PortFlag flagA, flagB, flagC, flagD;

// --- $B%"%I%l%9$*$h$S>:05@)8f%-%c%C%7%e(B ---
uint8_t a_adr01, a_adr02; 
uint8_t b_adr01, b_adr02; 
uint8_t c_adr01, c_adr02; 
uint8_t d_adr01, d_adr02; 

uint8_t vref;       // CV9$B$+$iFI$_9~$s$@>:05L\I8EE05$NogCM(B
uint8_t vref_cmd;   // $B8=:_$N(BPWM Duty$B=PNO;X<(CM(B ($B>:05%A%g%C%Q$N(BMOSFET$B6nF0MQ(B)
bool v_ready;       // $B>:05$,L\I8EE05$KC#$7$?$+$r<($9%U%i%0(B (true:$B40N;(B/$BE>495v2D(B)

// --- DCC$B%Q%1%C%H<u?.!&2r@OMQJQ?t(B ---
uint8_t dcc_data[6];   // $B<u?.$7$?(BDCC$B%Q%1%C%H$N3F%P%$%H$r3JG<$9$kG[Ns(B
uint8_t dcc_byte_cnt;  // $B<u?.$7$?%Q%1%C%H$NAm%P%$%H?t(B (3$B!A(B6$B%P%$%H(B)
uint8_t packet_ready;  // $B%Q%1%C%H$N<u?.40N;%U%i%0(B (1:$B40N;!"2r@O3+;O(B)

// --- $B%W%m%0%i%_%s%0%b!<%I!&J]8n5!G=MQJQ?t(B ---
uint8_t page_register = 0;      // Page$B%b!<%I%W%m%0%i%_%s%0;~$N8=:_;XDj%Z!<%8(B
uint16_t prog_mode_timer = 0;   // Prog-Silence $B5!9=MQ%?%$%^!<!#(B0$B$h$jBg$-$$4V$O>:05$r6/@)Dd;_$9$k!#(B

// =========================================================
// 5. $B4X?t%W%m%H%?%$%W@k8@(B
// =========================================================
void Init_System(void);
void Load_CV_Data(void);
void Load_LED_Status(void);
void Check_Boost_Control(void);
unsigned char measure_pulse(void);
void Process_DCC_Packet(void);
void Process_Service_Mode(void);
void Process_Operation_Mode(void);
void Reset_CV_To_Default(void);
void Send_Basic_Ack(void);
void Execute_Turnout(uint8_t ch, uint8_t is_c_cmd, uint8_t ee_addr);
void Delay_Turnout(void);
void Output_Control(void);

// ==========================================================
// $B!Z%a%$%s4X?t![(B $B%W%m%0%i%`$N5/E@(B
// ==========================================================
void main(void) {
    Init_System();        // $B%^%$%3%s$N%O!<%I%&%'%"=i4|@_Dj(B
    Load_CV_Data();       // EEPROM$B$+$i%"%I%l%9$d>:05@_Dj$r(BRAM$B$XFI$_9~$_(B
    Load_LED_Status();    // $BEE8;EjF~;~!"A02s$N%]%$%s%H(BLED$B>uBV(B($BE>49J}8~(B)$B$rI|85(B

    v_ready = false;      
    vref_cmd = 31;        // PWM$B$N=i4|(BDuty$BHf(B (PR2=63$B$NH>J,$NLs(B50%$B$+$i3+;O(B)

    while(1) {
        // ------------------------------------------------------
        // 1. $B>:05@)8f(B $B!u(B $B%W%m%0%i%_%s%0;~$N%N%$%:<WCG(B (Prog-Silence)
        // ------------------------------------------------------
        if (prog_mode_timer > 0) {
            prog_mode_timer--;
            CCPR1L = 0;      // $B%?%$%^!<:nF0Cf(B($B%W%m%0%i%_%s%0Cf(B)$B$O(BPWM$B$r6/@)Dd;_(B
            CHARGE_LED = 0;  // $B>:05Dd;_Cf$G$"$k$3$H$r;k3PE*$K$bL@<((B
        } else {
            // $BDL>o;~$O%3%s%G%s%5$N>:05%U%#!<%I%P%C%/@)8f$r9T$&(B
            Check_Boost_Control();
        }
        
        // ------------------------------------------------------
        // 2. DCC$B%Q%1%C%H$N<u?.BT5!(B ($B40A4(BC$B8@8lHG(B)
        // ------------------------------------------------------
        // $B?.9fES@d;~$O(B measure_pulse $BFb$G%?%$%`%"%&%H$7!"$9$0$KLa$C$F$-$^$9!#(B
        Process_DCC_Packet(); 
        
        // ------------------------------------------------------
        // 3. $B<u?.%Q%1%C%H$N2r@O$H<B9T(B
        // ------------------------------------------------------
        if (packet_ready) {
            uint8_t xor_sum = 0;
            // $B%Q%1%C%H$N=*C<$^$G(BXOR$B$r7W;;$7!"7k2L$,(B0$B$J$iDL?.%(%i!<$J$7(B
            for (uint8_t i = 0; i < dcc_byte_cnt; i++) {
                xor_sum ^= dcc_data[i];
            }
            
            if (xor_sum == 0 && dcc_byte_cnt >= 3) {
                // --- $B%j%;%C%H(B / $B%"%$%I%k(B $B%Q%1%C%H$N=hM}(B ---
                if (dcc_data[0] == 0x00) {
                    // $B%j%;%C%H$r<u?.$7$?$i!V%W%m%0%i%_%s%0%b!<%I7QB3Cf!W$H$_$J$7!"(B
                    // $BD@L[%?%$%^!<$r(B 1000 ($B?tIC4V(B) $B$K1dD9$7$F>:05:F3+$rKI$0!#(B
                    prog_mode_timer = 1000; 
                }
                else if (dcc_data[0] == 0xFF) {
                    // $B%"%$%I%k%Q%1%C%H$O2?$b$7$J$$(B
                }
                // --- $B%W%m%0%i%_%s%0(B($B%5!<%S%9%b!<%I(B)$B$NH=Dj(B ---
                else if ((dcc_data[0] & 0xF0) == 0x70) {
                    prog_mode_timer = 1000; // CV$BFI$_=q$-L?Na<u?.;~$b%?%$%^!<$r1dD9(B
                    Process_Service_Mode();
                }
                // --- $BDL>o$N%"%/%;%5%jE>49%Q%1%C%H$NH=Dj(B ---
                else {
                    Process_Operation_Mode();
                }
            }
        }
        
        // ------------------------------------------------------
        // 4. $B%]%$%s%HE>49$N<B9T(B
        // ------------------------------------------------------
        Output_Control();
    }
}

// ==========================================================
// $B!Z%O!<%I%&%'%"=i4|2=![(B
// ==========================================================
void Init_System(void) {
    LATA = 0x00; LATB = 0x00; LATC = 0x00; 
    OSCCON = 0xF0; // $B%/%m%C%/@_Dj(B: $BFbIt(B32MHz (8MHz$B%Y!<%9(B $B!_(B PLL4$BG\(B)

    // $BF~=PNOJ}8~@_Dj(B (1:$BF~NO(B, 0:$B=PNO(B)
    TRISA = 0x30; // RA4($BEE054F;k(BADC), RA5(DCC$B?.9f(B) $B$rF~NO%T%s$H$7$F@_Dj(B
    TRISB = 0x00; // $B%]!<%H(BB$B$O$9$Y$F=PNO(B
    TRISC = 0x00; // $B%]!<%H(BC$B$O$9$Y$F=PNO(B

    ANSELA = 0x10; // AN3(RA4)$B%T%s$N$_%"%J%m%05!G=(B($BEE054F;kMQ(B)$B$rM-8z2=(B
    ANSELB = 0x00; 
    ANSELC = 0x00; 

    // ADC(A/D$B%3%s%P!<%?(B)$B@_Dj(B
    ADCON1 = 0x20; // $BFI<h7k2L:85M$a(B($B>e0L(B8bit$B$@$1$GH=Dj(B), $BJQ49%/%m%C%/(BFosc/32, VDD$B%j%U%!%l%s%9(B
    ADCON0 = 0x0D; // $B4F;k%A%c%s%M%k$r(BAN3(RA4)$B$K@_Dj(B, ADC$BM-8z2=(B

    // PWM(CCP1)$B@_Dj(B ($B>:05%A%g%C%Q$N(BMOSFET$B6nF0MQ(B)
    CCP1CON = 0x0C; // $B%7%s%0%k(BPWM$B%b!<%IM-8z2=(B
    T2CON = 0x04;   // $B%W%j%9%1!<%i(B1:1, Timer2 ON
    TMR2 = 0x00;
    PR2 = 63;       // PWM$B<~4|@_Dj(B (32MHz$B6nF0;~!"Ls(B124kHz$B$N9bB.%9%$%C%A%s%0(B)
    CCPR1L = 31;    // $B=i4|(BDuty$B@_Dj(B (PR2$B$NH>J,$NLs(B50%)

    // Timer1 $B$N@_Dj(B (DCC$B%Q%k%9I}$N7WB,MQ(B)
    // 32MHz (Fosc/4 = 8MHz) $B$J$N$G!"%W%j%9%1!<%i(B1:8$B$G(B 1MHz (1$B%+%&%s%H(B=1us) $B$K$J$j$^$9!#(B
    T1CONbits.TMR1CS = 0;   // $B%/%m%C%/8;$rFbIt%/%m%C%/(B(Fosc/4)$B$K@_Dj(B
    T1CONbits.T1CKPS = 3;   // $B%W%j%9%1!<%i(B1:8$B!#$3$l$G!V(B1$B%+%&%s%H!a(B1us$B!W$K$J$k(B
    T1CONbits.TMR1ON = 1;   // Timer1$B$r5/F0(B

    // $B3d$j9~$_$O;HMQ$7$J$$(B($B%]!<%j%s%0J}<0(B)
    INTCON = 0x00;  
}

void Load_CV_Data(void) {
    a_adr01 = eeprom_read(EE_CV1_ADDR); a_adr02 = eeprom_read(EE_CV2_ADDR);
    b_adr01 = eeprom_read(EE_CV5_ADDR); b_adr02 = eeprom_read(EE_CV6_ADDR);
    c_adr01 = eeprom_read(EE_CV10_ADDR); c_adr02 = eeprom_read(EE_CV11_ADDR);
    d_adr01 = eeprom_read(EE_CV12_ADDR); d_adr02 = eeprom_read(EE_CV13_ADDR);
    vref = eeprom_read(EE_CV9_ADDR); 
}

void Load_LED_Status(void) {
    A_LEDOUT = (eeprom_read(EE_LED_A_ADDR) & 0x01) ? 1 : 0;
    B_LEDOUT = (eeprom_read(EE_LED_B_ADDR) & 0x01) ? 1 : 0;
    C_LEDOUT = (eeprom_read(EE_LED_C_ADDR) & 0x01) ? 1 : 0;
    D_LEDOUT = (eeprom_read(EE_LED_D_ADDR) & 0x01) ? 1 : 0;
}

// ==========================================================
// $B!Z>:05%A%g%C%Q2sO)$N(BADC$B%U%#!<%I%P%C%/@)8f![(B ($B%R%9%F%j%7%9F3F~HG(B)
// ==========================================================
void Check_Boost_Control(void) {
    uint8_t ad_result;
    ADCON0bits.GO_nDONE = 1;        // ADC$BJQ493+;O(B
    while(ADCON0bits.GO_nDONE);     
    ad_result = ADRESH;             // $B>e0L(B8$B%S%C%H$N$_<hF@$7$FHf3S(B

    // --- 1. PWM Duty$BHf$N%U%#!<%I%P%C%/@)8f(B ---
    if (ad_result < vref) {
        vref_cmd++;                 
        if (vref_cmd >= 32) vref_cmd = 31; // $B0[>o>:05$rKI$0$?$a>e8BLs(B50%$B%,!<%I(B
    } else {
        if (vref_cmd > 0) vref_cmd--;      
    }
    CCPR1L = vref_cmd;

    // --- 2. $B>:0540N;%U%i%0$H(BLED$B$N@)8f(B ($B%R%9%F%j%7%9@)8f(B) ---
    if (ad_result >= vref) {
        v_ready = true;             
        CHARGE_LED = 1;             
    } 
    // $BL\I8CM$+$iLs(B1.0V($BCM$G(B10)$B0J>e2<$,$C$?$i=<EECf%"%T!<%k(B
    else if (ad_result < (vref > 10 ? vref - 10 : 0)) {
        v_ready = false;            
        CHARGE_LED = 0;             
    }
}

// =========================================================
// $B!Z4X?t![(B DCC$B%Q%k%9I}7WB,(B ($B40A4(BC$B8@8l!&(BTimer1$BJ}<0(B)
// Timer1 (1$B%+%&%s%H(B=1us) $B$rMxMQ$7!"(BRA5$B%T%s$N%Q%k%9I}$r@53N$K7WB,$7$^$9!#(B
// $BLa$jCM(B: 1 ($BC;$$(B='1'$B%S%C%H(B), 0 ($BD9$$(B='0'$B%S%C%H(B), 2 ($B%?%$%`%"%&%H(B=$BL5?.9f(B)
// =========================================================
unsigned char measure_pulse(void) {
    // 1. RA5$B$,(BLow$B$K$J$k$N$rBT$D(B ($B%Q%k%9$NN)$A>e$,$j$rB*$($k=`Hw(B)
    TMR1H = 0; TMR1L = 0;
    while(PORTAbits.RA5 == 1) { 
        // 1us$B$G?J$`%?%$%^!<$N>e0L%P%$%H$,(B1$B$K$J$k(B(=256us$B7P2a(B)$B$H%?%$%`%"%&%HC&=P(B
        if (TMR1H > 0) return 2; 
    }
    
    // 2. RA5$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.RA5 == 0) { 
        if (TMR1H > 0) return 2; 
    }
    
    // 3. $B%Q%k%9$ND9$5(B(High$B$N4|4V(B)$B$r<BB,$9$k(B
    TMR1H = 0; TMR1L = 0;
    while(PORTAbits.RA5 == 1) { 
        if (TMR1H > 0) return 2; 
    }
    
    // 4. $B%Q%k%9I}$NH=Dj(B
    // DCC$B$N5,3J>e!"(B'1'$B$OJRGHLs(B58us$B!"(B'0'$B$OLs(B100us$B!#(B
    // Timer1$B$O(B1us$BC10L$J$N$G!"ogCM$r!V(B75us$B!W$H$7$FH=Dj$7$^$9!#(B
    if (TMR1L < 75) return 1;
    return 0;
}

// =========================================================
// $B!Z4X?t![(B DCC$B%Q%1%C%H<u?.=hM}(B ($B40A4(BC$B8@8lHG(B)
// $B%$%s%i%$%s%"%;%s%V%i$rGS=|$7!"(BC$B8@8l$NL@NF$J%k!<%W9=B$$G%Q%1%C%H$rAH$_N)$F$^$9!#(B
// =========================================================
void Process_DCC_Packet(void) {
    packet_ready = 0;
    unsigned char bit_val;
    unsigned char p_count = 0;
    
WAIT_PREAMBLE:
    p_count = 0;
    
PREAMBLE_LOOP:
    // $B%W%j%"%s%V%k(B('1'$B$NO"B3(B)$B$rC5$9(B
    bit_val = measure_pulse();
    if (bit_val == 2) return; // $B%?%$%`%"%&%H;~$O%a%$%s%k!<%W$X5"$j>:05@)8f$rB39T(B
    
    if (bit_val == 1) {
        p_count++;            // $BC;$$%Q%k%9$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; 
    }
    
    // --- $B$3$3$+$i%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++) {
        unsigned char current_byte = 0;
        
        // 1$B%P%$%H(B(8$B%S%C%H(B)$BJ,$N%G!<%?$rFI$_9~$_!":8%7%U%H$7$FAH$_N)$F$k(B
        for (unsigned char i = 0; i < 8; i++) {
            bit_val = measure_pulse();
            if (bit_val == 2) return;
            current_byte = (current_byte << 1) | bit_val;
        }
        dcc_data[dcc_byte_cnt] = current_byte; // $B%P%C%U%!$K3JG<(B
        
        // $B%P%$%H$H%P%$%H$N4V$K$"$k!V%;%Q%l!<%?%S%C%H!W$N3NG'(B
        bit_val = measure_pulse();
        if (bit_val == 2) return;
        
        if (bit_val == 1) {
            // $B%;%Q%l!<%?$,(B '1' ($B%Q%1%C%H%(%s%I%S%C%H(B) $B$J$i!"$3$N%Q%1%C%H$O=*N;(B
            dcc_byte_cnt++; // $B8=:_3JG<$7$?%P%$%H$rD9$5$H$7$F%+%&%s%H$K4^$a$k(B
            
            // $B%Q%1%C%H$O:GDc(B3$B%P%$%HI,MW(B
            if (dcc_byte_cnt >= 3) {
                packet_ready = 1; // $B<u?.40N;%U%i%0$rN)$F$F%a%$%s%k!<%W$K0z$-EO$9(B
            }
            return;
        }
        // $B%;%Q%l!<%?$,(B '0' $B$J$i!"$^$@<!$N%G!<%?%P%$%H$,B3$/$?$a%k!<%W$r7QB3(B
    }
}

// ==========================================================
// $B!ZDL>o%Q%1%C%H(B($B%"%/%;%5%jE>49L?Na(B)$B$N2r@O![(B
// ==========================================================
void Process_Operation_Mode(void) {
    // $BBh(B1$B%P%$%H$,(B 10xxxxxx [0x80$B!A(B0xBF] $B$G;O$^$k$J$i%"%/%;%5%jE>49L?Na(B
    if ((dcc_data[0] & 0xC0) == 0x80) {
        
        uint8_t addr1 = dcc_data[0];
        // $BHf3S$N$?$a!"Bh(B2$B%P%$%H$N:G2<0L%S%C%H(B(t/c$B@)8f>pJs(B)$B$r(B0$B$K8GDj(B
        uint8_t addr2 = dcc_data[1] & 0xFE; 
        
        // $BE>49L?Na(B(t/c)$B$NCj=P(B ($B:G2<0L%S%C%H(B 1='c'(Closed/$BD>?J(B), 0='t'(Turnout/$BJ,4t(B))
        uint8_t cmd_c = dcc_data[1] & 0x01; 

        // $B<+J,$N%"%I%l%9$H0lCW$7$?2sO)$NE>49%U%i%0$r%;%C%H(B
        if ((addr1 == a_adr01) && (addr2 == a_adr02)) {
            flagA.c_cmd = cmd_c; flagA.t_cmd = !cmd_c; flagA.data_rcv = 1;
        }
        else if ((addr1 == b_adr01) && (addr2 == b_adr02)) {
            flagB.c_cmd = cmd_c; flagB.t_cmd = !cmd_c; flagB.data_rcv = 1;
        }
        else if ((addr1 == c_adr01) && (addr2 == c_adr02)) {
            flagC.c_cmd = cmd_c; flagC.t_cmd = !cmd_c; flagC.data_rcv = 1;
        }
        else if ((addr1 == d_adr01) && (addr2 == d_adr02)) {
            flagD.c_cmd = cmd_c; flagD.t_cmd = !cmd_c; flagD.data_rcv = 1;
        }
    }
}

// ==========================================================
// $B!Z%3%^%s%I%9%F!<%7%g%s$X$N1~Ez(B (Basic ACK$BAw?.(B)$B![(B
// $B3N<B$KG'<1$5$;$k$?$a!"%3%^%s%I%9%F!<%7%g%s$N(BADC$B$N0BDj$rBT$A!"(B
// $B5,Dj$NEEN.(B(60mA$B0J>e(B)$B$r>CHq$5$;$^$9!#(B
// ==========================================================
void Send_Basic_Ack(void) {
    __delay_ms(1); // $B%3%^%s%I%9%F!<%7%g%sB&$N(BADC($BEEN.7WB,4o(B)$B$,0BDj$9$k$N$rBT$D(B
    A_OUT2 = 0;
    A_OUT1 = 1;    // $B2sO)(BA$B$rDLEE$5$;$F(BACK$BEEN.$rH/@8$5$;$k(B
    __delay_ms(6); // NMRA$B5,3J$GDj$a$i$l$?1~Ez;~4V(B($BLs(B6ms)
    A_OUT1 = 0;
}

// ==========================================================
// $B!Z(BCV$BCM$r9)>l=P2Y>uBV$K%j%;%C%H![(B (CV8$B$K!V(B8$B!W=q$-9~$_;~$KH/F0(B)
// ==========================================================
void Reset_CV_To_Default(void) {
    eeprom_write(EE_CV1_ADDR,  129); eeprom_write(EE_CV2_ADDR,  248);
    eeprom_write(EE_CV3_ADDR,  125); eeprom_write(EE_CV5_ADDR,  129);
    eeprom_write(EE_CV6_ADDR,  250); eeprom_write(EE_CV9_ADDR,  200); 
    eeprom_write(EE_CV10_ADDR, 129); eeprom_write(EE_CV11_ADDR, 252);
    eeprom_write(EE_CV12_ADDR, 129); eeprom_write(EE_CV13_ADDR, 254);
    Load_CV_Data(); // RAM$B%-%c%C%7%e$b:G?7$N=i4|CM$G>e=q$-$9$k(B
}

// ==========================================================
// $B!Z%5!<%S%9%b!<%I(B (CV$B%W%m%0%i%_%s%0=hM}(B)$B![(B
// KATO D101(DCS50K)$BEy$G;H$o$l$k!V(BPage Mode$B!W$*$h$S!V(BDirect Mode (Bit/Byte)$B!W(B
// $B$NN>J}$K40A4BP1~$7!"$"$i$f$k%3%^%s%I%9%F!<%7%g%s$G$NFI=q$r2DG=$K$7$^$9!#(B
// ==========================================================
void Process_Service_Mode(void) {
    uint8_t instruction = dcc_data[0]; // $BBh(B1$B%P%$%H(B ($BL?Na(B)
    
    // $B4X?tFb$G$bL@<(E*$K>:05(B(PWM)$B$rDd;_$7!"(BACK$BFI$_<h$jK832(B(Err 02$BEy(B)$B$r40A4$KKI$.$^$9!#(B
    CCPR1L = 0; 
    CHARGE_LED = 0;
    
    // --------------------------------------------------------
    // $B!Z(B1$B![(BPAGE $B%b!<%I(B ($B%Q%1%C%HD9$,(B3$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;           // $B%l%8%9%?HV9fEy$K;HMQ(B
        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$X$N%"%/%;%9(B
            if (page_register == 0) { packet_ready = 0; return; }
            cv_addr = (page_register - 1) * 4 + command; // $B%Z!<%8$H%l%8%9%?$+$i(BCV$B%"%I%l%9$r;;=P(B
        } 
        else if (command == 4) { // $B%3%s%U%#%.%e%l!<%7%g%s%l%8%9%?(B (CV29)
            cv_addr = EE_CV29_ADDR;
        }
        else if (command == 0x05) { // $B%Z!<%8%l%8%9%?$X$N=q$-9~$_(B($B<!$X$N=`Hw(B)
            if (is_write) page_register = target_data;
            if (!is_write && page_register != target_data) { packet_ready = 0; return; }
            Send_Basic_Ack();
            packet_ready = 0; return;
        }
        else { // R7(CV7), R8(CV8) $BEy$ND>@\%"%/%;%9(B
            cv_addr = command;
        }

        if (is_write) {
            if (cv_addr == EE_CV8_ADDR && target_data == 8) {
                Reset_CV_To_Default();
                Send_Basic_Ack();
            } else {
                // $B!Z0BA4BP:v![(BCV9($B>:05L\I8(B)$B$X(B241$B0J>e$N2aEE05@_Dj$r%V%m%C%/(B
                if (cv_addr == EE_CV9_ADDR && target_data > 240) target_data = 240;
                
                Send_Basic_Ack(); // $B%?%$%`%"%&%H$rKI$0$?$a!"(BEEPROM$B=q$-9~$_$NA0$K(BACK$B$rJV$9(B
                eeprom_write(cv_addr, target_data);
                Load_CV_Data();
            }
        } else { // Verify ($BFI$_9~$_3NG'(B)
            if (eeprom_read(cv_addr) == target_data) Send_Basic_Ack();
        }
    }
    // ---------------------------------------------------------
    // $B!Z(B2$B![(BDIRECT $B%b!<%I(B (Byte$BC10LA`:n(B) ($B%Q%1%C%HD9$,(B4$B%P%$%H(B)
    // 0xF4$B%^%9%/$K$h$j!"2<0L(B2$B%S%C%H$N%"%I%l%9>pJs$rL5;k$7$FL?Na$rH=Dj(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];     // Direct Mode$B$G$OBh(B2$B%P%$%H$,$=$N$^$^(BCV$B%"%I%l%9(B
        uint8_t target_data = dcc_data[2]; // $BBh(B3$B%P%$%H$,%G!<%?(B

        if (is_write) {
            if (cv_addr == EE_CV8_ADDR && target_data == 8) {
                Reset_CV_To_Default();
                Send_Basic_Ack();
            } else {
                if (cv_addr == EE_CV9_ADDR && target_data > 240) target_data = 240;
                Send_Basic_Ack();
                eeprom_write(cv_addr, target_data);
                Load_CV_Data();
            }
        } else {
            if (eeprom_read(cv_addr) == target_data) Send_Basic_Ack();
        }
    }
    // ---------------------------------------------------------
    // $B!Z(B3$B![(BDIRECT $B%b!<%I(B (Bit Manipulation / $B%S%C%HC10LA`:n(B)
    // $B%Q%1%C%H7A<0(B: 0111 10xx (0x78$B!A(B0x7B)
    // DSairLITE$B$d(BDCS50K$B$GB?MQ$5$l$k!"(BCV$B$NFCDj%S%C%H$@$1$rFI$_=q$-$9$k%b!<%I(B
    // ---------------------------------------------------------
    else if (dcc_byte_cnt == 4 && (instruction & 0xFC) == 0x78) {
        uint8_t cv_addr = dcc_data[1]; 
        // $BBh(B3$B%P%$%H(B(data2)$B$N9=@.(B: "111K DBBB"
        uint8_t k_bit = (dcc_data[2] & 0x10) ? 1 : 0; // K: 1=Write, 0=Verify
        uint8_t d_bit = (dcc_data[2] & 0x08) ? 1 : 0; // D: $B>H9g(B/$B=q$-9~$_$9$kL\I8$N%S%C%HCM(B(1/0)
        uint8_t bit_pos = dcc_data[2] & 0x07;         // BBB: $BBP>]$H$J$k%S%C%H0LCV(B(0$B!A(B7)
        
        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) {
            // --- Bit Verify ($B;XDj%S%C%H$,L\I8CM$H0lCW$9$k$+!)(B) ---
            if (actual_bit == d_bit) Send_Basic_Ack();
        } else {
            // --- Bit Write ($B;XDj%S%C%H$@$1$r=q$-49$($k(B) ---
            Send_Basic_Ack(); 
            if (d_bit) current_cv_val |= bit_mask;  // $B;XDj%S%C%H$r!V(B1$B!W$K$9$k(B
            else       current_cv_val &= ~bit_mask; // $B;XDj%S%C%H$r!V(B0$B!W$K$9$k(B
            
            if (cv_addr == EE_CV9_ADDR && current_cv_val > 240) current_cv_val = 240;
            
            eeprom_write(cv_addr, current_cv_val);
            Load_CV_Data();
        }
    }
    packet_ready = 0; // $B=hM}40N;$H$7$F%U%i%0$r2<$m$9(B
}

// ==========================================================
// $B!Z=PNO:nF0;~4V(B(CV3)$B$N%&%'%$%H=hM}![(B
// $B@_Dj$5$l$?;~4V$@$1%]%$%s%H%^%7%s$XDLEE$r0];}$7$^$9!#(B
// ==========================================================
void Delay_Turnout(void) {
    uint8_t delay_ms = eeprom_read(EE_CV3_ADDR);
    // CV3$B$N?tCM(B 1 $B$K$D$-(B 1ms $B$NCY1d$r%k!<%W$G@8@.(B
    for (uint8_t i = 0; i < delay_ms; i++) {
        __delay_ms(1);
    }
}

// ==========================================================
// $B!Z%]%$%s%HE>49$H(BLED$B99?7$N<B9T4X?t![(B
// $B3F2sO)6&DL$NE>49%m%8%C%/$G$9!#(B
// ==========================================================
void Execute_Turnout(uint8_t ch, uint8_t is_c_cmd, uint8_t ee_addr) {
    CHARGE_LED = 0; // $BEE059_2<$rL@<($9$k$?$a!"%A%c!<%840N;(BLED$B$r0lC6>CEt(B

    // 1. $B;XDj$5$l$?2sO)$N%T%s$r=PNO$7$FDLEE3+;O(B ($B"($3$N;~E@$G$O(BLED$BI=<($O0JA0$N>uBV$N$^$^0];}(B)
    if (ch == 0) { A_OUT1 = !is_c_cmd; A_OUT2 = is_c_cmd; }
    else if (ch == 1) { B_OUT1 = !is_c_cmd; B_OUT2 = is_c_cmd; }
    else if (ch == 2) { C_OUT1 = !is_c_cmd; C_OUT2 = is_c_cmd; }
    else if (ch == 3) { D_OUT1 = !is_c_cmd; D_OUT2 = is_c_cmd; }

    // 2. $BDLEE0];}%&%'%$%H(B ($B%]%$%s%H%^%7%s$N%=%l%N%$%I$,F0$-@Z$k$N$rBT$D(B)
    Delay_Turnout();

    // 3. $B=PNO$r(BOFF ($BDLEE$7B3$1$k$H%3%$%k$,>FB;$9$k$?$a(B) $B!u(B $BE>4940N;$H$7$F(BLED$BI=<($r?7$7$$J}8~$K99?7(B
    if (ch == 0) { A_OUT1 = 0; A_OUT2 = 0; A_LEDOUT = is_c_cmd; }
    else if (ch == 1) { B_OUT1 = 0; B_OUT2 = 0; B_LEDOUT = is_c_cmd; }
    else if (ch == 2) { C_OUT1 = 0; C_OUT2 = 0; C_LEDOUT = is_c_cmd; }
    else if (ch == 3) { D_OUT1 = 0; D_OUT2 = 0; D_LEDOUT = is_c_cmd; }

    // 4. $B8=:_$N(BLED$B>uBV(B($BE>49J}8~(B)$B$r(BEEPROM$B$KJ]B8(B ($BEE8;:FEjF~;~$NI|85MQ(B)
    // $B!Z(BEEPROM$B<wL?J]8n![(B $B4{$KJ]B8$5$l$F$$$kCM$H0c$&(B($B<B:]$K%]%$%s%H$,@Z$jBX$o$C$?(B)$B>l9g$N$_=q$-9~$`(B
    if (eeprom_read(ee_addr) != is_c_cmd) {
        eeprom_write(ee_addr, is_c_cmd);
    }
}

// ==========================================================
// $B!Z=PNO@)8f$N%a%$%s4IM}![(B
// $B%a%$%s%k!<%W$+$i8F$P$l!"%U%i%0$,N)$C$F$$$k2sO)$NE>49$r=gHV$K<B9T$7$^$9!#(B
// ==========================================================
void Output_Control(void) {
    // $B%3%s%G%s%5$N>:05$,40N;$7$F$$$J$1$l$P!"L?Na$r<u?.$7$F$$$F$bE>49$r<B9T$;$:BT5!$9$k(B
    if (!v_ready) return; 

    // A$B!A(BD$B$N=gHV$K%U%i%0$r3NG'$7!"L?Na$,$"$l$P(B Execute_Turnout $B$r8F$S=P$7$F<B9T(B
    // $B<B9T8e$O%U%i%0$r2<$m$7!"%3%s%G%s%5$NEE2Y$r>CHq$7$?$?$a>:0540N;%U%i%0(B(v_ready)$B$r2r=|$9$k(B
    if (flagA.data_rcv) {
        Execute_Turnout(0, flagA.c_cmd, EE_LED_A_ADDR);
        flagA.data_rcv = 0; v_ready = false;
    }
    else if (flagB.data_rcv) {
        Execute_Turnout(1, flagB.c_cmd, EE_LED_B_ADDR);
        flagB.data_rcv = 0; v_ready = false;
    }
    else if (flagC.data_rcv) {
        Execute_Turnout(2, flagC.c_cmd, EE_LED_C_ADDR);
        flagC.data_rcv = 0; v_ready = false;
    }
    else if (flagD.data_rcv) {
        Execute_Turnout(3, flagD.c_cmd, EE_LED_D_ADDR);
        flagD.data_rcv = 0; v_ready = false;
    }
}