たまりば

パソコン・インターネット パソコン・インターネット三鷹市 三鷹市

最弱のPICマイコンでカレンダー
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。
PIC10F200カレンダー_回路

「Y?>」に対して年を4桁で、「M?>」に対して月を2桁で入力すると、当該月のカレンダーを出力してまた「Y?>」から入力待ちとなる。
表示はこんな感じ。(ローカルエコーON)
PIC10F200カレンダー_コンソール
プログラム容量は余裕があるのでもうちょっとまともなメッセージにしてもよかったかもしれない。

以下プログラム。
プログラム容量は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<     btfss STATUS,C
    iorlw 1<     movwf GPIO ;write 7+9n
    decfsz cnt232,F
    goto tx232loop
    goto $+1
    goto $+1
    nop
    bcf GPIO,TXPIN ;stop
    retlw 0

;RS232Cで1バイト受信し、下位ニブルのみ返す
rx232:
    btfss GPIO,3 ;start [0,3)
    goto $-1
    movlw 0x80
    movwf rxdat
    bcf STATUS,C
rxloop:
    goto $+1
    movlw 0x0F
    rrf rxdat,F
    btfss GPIO,RXPIN ;read [9,12)+9n
    bsf rxdat,7
    btfss STATUS,C
    goto rxloop
    andwf rxdat,F
    retlw 0

    end

おまけで機械語。1ワード(=1命令)が12bitで、16進数にすると3文字という独特の見た目が気に入っている。
    0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
0x 025 CC8 066 006 002 075 078 C59 99E C3F 99E C3E 99E 9AE 211 032
1x 1F2 9AE 211 1F2 9AE 211 033 403 373 373 1F3 373 9AE 211 1F3 97B
2x C4D 99E C3F 99E C3E 99E 9AE 211 E01 743 C0A 034 9AE 211 E0F 1F4
3x 213 643 212 E03 743 A3B C03 094 C0C 703 1F4 313 03A 31A E3F 1F3
4x 352 E06 0B3 0D4 980 037 0D4 98F 1F3 C07 0B3 603 A4A 1F3 C08 036
5x 293 0B6 035 97B A5B C20 99E C20 99E C20 99E 2F5 A55 079 C07 1F9
6x C06 723 0B9 C20 99E 399 E0F 643 C20 743 D30 99E 219 E0F D30 99E
7x 0F7 643 A79 2F6 A5E C07 036 97B A5E 97B A05 C0D 99E C0A 99E 800
8x 1E2 81F 81C 81F 81E 81F 81E 81F 81F 81E 81F 81E 81F 81F 81D 1E2
9x 807 80A 80A 80D 808 80B 80D 809 80C 807 80A 80C 80D 809 030 506
Ax 578 330 206 EFE 703 D01 026 2F8 AA1 AAA AAB 000 406 800 766 AAE
Bx C80 031 403 AB4 C0F 331 766 5F1 703 AB3 171 800 --- --- --- ---
Cx --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
Dx --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
Ex --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
Fx --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- Cxx