// PIC16F689 8bit SC2004 接続 文字表示

#include 

__CONFIG(INTIO & WDTDIS & PWRTEN & MCLREN & UNPROTECT);

#define _XTAL_FREQ 4000000 

#define RS RA1  
#define E  RA0  

// ----------------------------------------------------
// 【設定】4行目の好きな位置をここに指定してください
// ----------------------------------------------------
#define START_ADDRESS 0x80

// ----------------------------------------------------
// 【RAM不足対策】メモリを圧迫しないよう、固定配列や長い文字列は
// 関数の外(グローバル領域)で「static const」として定義します
// ----------------------------------------------------
static const unsigned char line_addresses[] = {0x80, 0xC0, 0x94, 0xD4};
static const char str[] = "CQ CQ DE JA3RUA PSE K"; 

void main(void) {
    // ----------------------------------------------------
    // 変数の宣言(関数の最初)
    // ----------------------------------------------------
    int i;
    int col_count = 0;       // 現在の行で何文字送ったかを数えるカウンター
    int current_line = 0;    // 現在の物理的な行(0=1行目, 1=2行目, 2=3行目, 3=4行目)
    int first_line_limit = 0;

    // ----------------------------------------------------
    // 1. PICの初期設定
    // ----------------------------------------------------
    ANSEL   = 0x00; 
    ANSELH  = 0x00;
    CM1CON0 = 0x00; 
    CM2CON0 = 0x00;
    
    TRISA0 = 0;    
    TRISA1 = 0;    
    TRISC  = 0x00; 

    PORTA = 0x00;  
    PORTC = 0x00;

    // ----------------------------------------------------
    // 2. LCDの初期化処理
    // ----------------------------------------------------
    __delay_ms(30);     
    RS = 0; PORTC = 0x38; E = 1; __delay_us(2); E = 0; __delay_ms(2); 
    RS = 0; PORTC = 0x0C; E = 1; __delay_us(2); E = 0; __delay_ms(2); 
    RS = 0; PORTC = 0x06; E = 1; __delay_us(2); E = 0; __delay_ms(2); 
    RS = 0; PORTC = 0x01; E = 1; __delay_us(2); E = 0; __delay_ms(2); 

    // ----------------------------------------------------
    // 3. 開始アドレスから現在の物理行を特定
    // ----------------------------------------------------
    if (START_ADDRESS >= 0xD4) {
        current_line = 3; // 4行目
    } else if (START_ADDRESS >= 0xC0) {
        current_line = 1; // 2行目
    } else if (START_ADDRESS >= 0x94) {
        current_line = 2; // 3行目
    } else if (START_ADDRESS >= 0x80) {
        current_line = 0; // 1行目
    } else {
        current_line = 0; 
    }

    // 最初の行(4行目)の右端までの残り文字数を自動計算
    first_line_limit = 20 - (START_ADDRESS - line_addresses[current_line]);

    // 最初の表示位置コマンドを送信
    RS = 0; PORTC = START_ADDRESS; E = 1; __delay_us(2); E = 0; __delay_ms(2);

    // ----------------------------------------------------
    // 4. 無限ループ自動改行送信ロジック(4行目?1行目?2行目?3行目)
    // ----------------------------------------------------
    for(i = 0; str[i] != '\0'; i++) {  //for
        
        // 文字データの送信
        RS = 1; PORTC = str[i]; E = 1; __delay_us(2); E = 0; __delay_us(50);
        
        col_count++; // 表示文字数をカウント

        // 行の右端に達した場合
        if (col_count == first_line_limit) {  //if1
            current_line++; // 次の物理行へ進む
            
            // 4行目(3)の右端を超えたら、1行目(0)にループバックさせる
            if (current_line > 3) {  //if2
                current_line = 0; 
            }//if2
            
            // 安全対策:画面1周分(80文字)を超えて上書きしそうなら終了
            if (i >= 80) {   //if3
                break;
            }//if3
            
            // 次の行の先頭アドレス(1行目なら0x80、2行目なら0xC0)を送信
            RS = 0; PORTC = line_addresses[current_line]; E = 1; __delay_us(2); E = 0; __delay_ms(2);
            
            // 以降の行は「20文字ぴったり」で改行
            first_line_limit = 20; 
            col_count = 0; // カウンターリセット
        }//if1
    }  //for

    // ----------------------------------------------------
    // 5. メインループ(画面の点滅動作)
    // ----------------------------------------------------
    while(1) {  //while
        RS = 0; PORTC = 0x08; E = 1; __delay_us(2); E = 0; __delay_ms(2);
        for(i = 0; i < 5; i++) { __delay_ms(100); }

        RS = 0; PORTC = 0x0C; E = 1; __delay_us(2); E = 0; __delay_ms(2);
        for(i = 0; i < 5; i++) { __delay_ms(100); }
    }//while
}