最弱のPICマイコンでカレンダー

いかづちSqueak

2014年10月30日 01:12

「プロ生ちゃんカレンダー プログラミング プチコンテスト 2014」という面白そうなものを知った。
カレンダーに載せる、"カレンダーを表示するソースコード"を募集するコンテストである。
思えばカレンダーのプログラムを書いたことが無かったので自分も何か書いてみたくなり、さて何で書くかと考えたところ、やはり最近マイブームなPIC10F200でいくことにした。
(その後よく考えてみたらJavaScriptでカレンダーを書いたことがあったが、まあ自力で閏年判定とかが初めてということで)

コードは末尾に載せたが、中でも今回自分で書いてて気に入っているポイントをいくつか挙げると、
・数値を受け取るそばから要らない部分を捨てて保存する(年の1000の位は2倍して100の位に足す)
・不正な値は完全無視(月の10の位は最下位bitしか見てないとか)
・6命令で閏年判定
movf yearl,W
btfsc STATUS,Z
movf yearh,W
andlw 0x03
btfss STATUS,Z
goto notleap
といったあたり。
あとはいつも使ってるコードだけどシリアル送受信は自信作。

使い方は、シリアルポートに抵抗だけ挟んで写真のように直結し、電源ON。


「Y?>」に対して年を4桁で、「M?>」に対して月を2桁で入力すると、当該月のカレンダーを出力してまた「Y?>」から入力待ちとなる。
表示はこんな感じ。(ローカルエコーON)

プログラム容量は余裕があるのでもうちょっとまともなメッセージにしてもよかったかもしれない。

以下プログラム。
プログラム容量は188ワード。PIC10F200の容量256ワードのうち使用率75%ほど。
(なお最初書き上げたときは201ワードだったのだが、コードにコメントなどつけて整理していたらいつの間にかここまで縮んでしまった。)
使用メモリは11バイト。16バイトの7割。
どちらも余裕であった。
;### PIC10F200 Calendar###
;by Ikadzuchi (@Pleist)
    list p=10F200
    #include p10f200.inc
    radix dec

    __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_OFF

    cblock 0x10
        txdat, rxdat
        yearh, yearl, month
        cnt, dowcnt, daycnt, cnt232
        day
        temp
    endc

;ピン
#define TXPIN 0
#define RXPIN 3

    org 0
init:
    movwf OSCCAL ;クロック補正値セット
    movlw b'11001000'
    ;設定データ。プルアップOFF、他Don'tCare
    ;ちょうど下位3bitがDon'tCareなのでI/O設定と共用
    clrf GPIO ;出力ピンを全Lowに
    tris GPIO ;I/Oモード設定
    option ;設定

main:
    clrf cnt
    clrf cnt232

;"Y?>"
    movlw 'Y'
    call tx232
    movlw '?'
    call tx232
    movlw '>'
    call tx232

;年4桁受信: abcd
;年上位 = 10a+b ≡ 2a+b
    call rx232
    movf rxdat,W
    movwf yearh ;*1
    addwf yearh,F ;*2
    call rx232
    movf rxdat,W
    addwf yearh,F
;年下位 = 10c+d
    call rx232
    movf rxdat,W
    movwf yearl ;*1
    bcf STATUS,C
    rlf yearl,F ;*2
    rlf yearl,F ;*4
    addwf yearl,F ;*5
    rlf yearl,F ;*10
    call rx232
    movf rxdat,W
    addwf yearl,F

;"M?>"
    call newline
    movlw 'M'
    call tx232
    movlw '?'
    call tx232
    movlw '>'
    call tx232

;月2桁受信: ef
    call rx232
    movf rxdat,W
    andlw 0x01 ;1っぽければ10をセット
    btfss STATUS,Z
    movlw .10
    movwf month
    call rx232
    movf rxdat,W
    andlw 0x0F
    addwf month,F ;C0

;閏年判定
    movf yearl,W ;年下位を取り、
    btfsc STATUS,Z ;0なら
    movf yearh,W ;年上位を取り、
    andlw 0x03 ;下位2bitが
    btfss STATUS,Z ;0でなければ
    goto notleap ;平年。

;閏年1,2月を13,14月扱い
    movlw .3
    subwf month,W
    movlw .12
    btfss STATUS,C
    addwf month,F
notleap:

;年ごとの曜日の基準を算出
;年下位 + 年下位/4 + (年上位%4)*2
    rrf yearl,W
    movwf temp
    rrf temp,W
    andlw 0x3F
    addwf yearl,F
    rlf yearh,W
    andlw 0x06
    subwf yearl,F
#define doworg yearl ;名前変更: DoW origin

;当月日数を取得
    decf month,W
    call getdaysinmonth
    movwf daycnt
;当月の曜日の差分を取得し基準に足す
    decf month,W
    call getfirstdayinmonth
    addwf doworg,F

;%=7
    movlw .7
mod7loop:
    subwf doworg,F
    btfsc STATUS,C
    goto mod7loop
    addwf doworg,F

;ループ用にNと(6-N)を生成、コードの都合上+1
    movlw .8
    movwf dowcnt
    incf doworg,W
    subwf dowcnt
    movwf cnt

    call newline

;月初めの日まで空白で埋める
    goto dayadjstart
dayadjloop:
    movlw ' '
    call tx232
    movlw ' '
    call tx232
    movlw ' '
    call tx232
dayadjstart:
    decfsz cnt,F
    goto dayadjloop

    clrf day
dayoutloop:
;表示用日(BCD)を++
    movlw .7
    addwf day,F
    movlw .6
    btfss STATUS,DC
    subwf day,F

;1日分の表示
    movlw ' '
    call tx232
    swapf day,W
    andlw 0x0F
    btfsc STATUS,Z
    movlw ' '
    btfss STATUS,Z
    iorlw 0x30
    call tx232
    movf day,W
    andlw 0x0F
    iorlw 0x30
    call tx232

    decf daycnt,F
    btfsc STATUS,Z
    goto break ;1月分出力完了で抜ける

    decfsz dowcnt,F
    goto dayoutloop ;日ループ

    movlw .7
    movwf dowcnt
    call newline
    goto dayoutloop ;週ループ
break:
    call newline

    goto main

;### subroutine ###

;改行を出力
newline:
    movlw '\r'
    call tx232
    movlw '\n'
    call tx232
    retlw 0

;当該月の日数を返す
getdaysinmonth:
    addwf PCL,F
    dt .31, .28, .31, .30, .31, .30 ;1~12月
    dt .31, .31, .30, .31, .30, .31
    dt .31, .29 ;閏年1,2月

#define ADJ .7 ;負にならないよう補正するため
;基準の年の当該月1日の曜日を返す
getfirstdayinmonth:
    addwf PCL,F
    dt 0+ADJ, 3+ADJ, 3+ADJ, 6+ADJ, 1+ADJ, 4+ADJ ;1~12月
    dt 6+ADJ, 2+ADJ, 5+ADJ, 0+ADJ, 3+ADJ, 5+ADJ
    dt 6+ADJ, 2+ADJ ;閏年1,2月

;RS232Cで1バイト送信する
tx232:
    movwf txdat
    bsf GPIO,TXPIN ;start 0
    bsf cnt232,3
tx232loop:
    rrf txdat,F
    movf GPIO,W
    andlw ~(1

関連記事