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割。
どちらも余裕であった。
おまけで機械語。1ワード(=1命令)が12bitで、16進数にすると3文字という独特の見た目が気に入っている。
カレンダーに載せる、"カレンダーを表示するソースコード"を募集するコンテストである。
思えばカレンダーのプログラムを書いたことが無かったので自分も何か書いてみたくなり、さて何で書くかと考えたところ、やはり最近マイブームな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<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