commit df9606e8cee0644150d98700d0ed483253e2b081
parent ce840aea4ad8b15128c3043e0d6c6dc3637c60f7
Author: Matsuda Kenji <info@mtkn.jp>
Date: Sat, 10 Feb 2024 09:19:53 +0900
fix indentation
Diffstat:
5 files changed, 2604 insertions(+), 2608 deletions(-)
diff --git a/data/weblog b/data/weblog
@@ -131,3 +131,5 @@
1707404400 /index.html
1707404400 /kitchen/index.html
1707404400 /kitchen/recipe/miso.html
+1707490800 /computer/rp2040_1.html
+1707490800 /computer/rp2040_1.html
diff --git a/man/computer/rp2040_1.html b/man/computer/rp2040_1.html
@@ -574,8 +574,7 @@ Table 7.4. Vector table format
RP2040のベクターテーブルはM0PLUS: VTOR(<code>0xe0000000 + 0xed08</code>)という\
レジスタに書き込むことで設定する。以上をまとめると以下のコードになる:\
</p>
-<pre><code>\
- ldr r0, flash_main
+<pre><code> ldr r0, flash_main
ldr r1, m0plus_vtor
str r0, [r1, #0] // vector table
ldr r1, [r0, #4] // entry point
@@ -721,8 +720,7 @@ GPIO25番の機能を選択するにはIO_BANK0_BASE(<code>0x40014000</code>)か
の番号を書き込めばいい。データシートの「2.19.2 Function Select」にある\
表を見ると、GPIO25番のSIOは5である:\
</p>
-<pre><code>\
- // set gpio functions
+<pre><code> // set gpio functions
ldr r3, io_bank0_base
mov r0, #5 // sio
mov r1, #0xcc
@@ -742,8 +740,7 @@ GPIO25番がSIOになったので、次にこのピンからの出力を有効
<code>0x24</code>バイト目のSIO: GPIO_OEレジスタの該当するビット\
(25番のピンなので25番ビット)を<code>0x1</code>にする:
</p>
-<pre><code>
- // enable gpio output
+<pre><code> // enable gpio output
ldr r3, sio_base
mov r0, #1
lsl r0, r0, #25 // gpio25
@@ -783,8 +780,7 @@ SIO: GPIO_OUT_XORレジスタがそれである。\
面倒なのでとりあえずこれで:\
</p>
-<pre><code>\
- // blink led on gpio25
+<pre><code> // blink led on gpio25
ldr r4, sio_base
mov r5, r0 // r0 = 1 << 25
loop:
@@ -839,7 +835,7 @@ SECTIONS
<p>
以上のソースコードは以下のように配置している:
</p>
-<pre><code>\
+<pre>\
rp2040
├── ex1
│ ├── Makefile
@@ -850,7 +846,7 @@ rp2040
├── Makefile
├── bin2uf2.c
└── bincrc.c
-</code></pre>
+</pre>
<p>
toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>\
でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。\
diff --git a/pub/computer/rp2040_1.html b/pub/computer/rp2040_1.html
@@ -474,7 +474,7 @@ Table 7.4. Vector table format
</blockquote>
<p>
また、ベクターテーブルはメインのプログラムの先頭に置くことにする。メインのプログラムはFlash Second Stageが占有する256バイトの直後、フラッシュROMの257バイト目から配置することにする。RP2040のベクターテーブルはM0PLUS: VTOR(<code>0xe0000000 + 0xed08</code>)というレジスタに書き込むことで設定する。以上をまとめると以下のコードになる:</p>
-<pre><code>ldr r0, flash_main
+<pre><code> ldr r0, flash_main
ldr r1, m0plus_vtor
str r0, [r1, #0] // vector table
ldr r1, [r0, #4] // entry point
@@ -554,7 +554,7 @@ resets_base:
<p>RP2040のGPIOにはそれぞれ複数の機能が用意されていて、どれを使うかはソフトウェアから選択できる。利用できる機能の一覧と各機能の説明はデータシートの「2.19.2 Function Select」に詳しく書いてある。ここではGPIO25番のピンをSIO(Single-cycle IO)として利用する。同じCPUが載っているRaspberry Pi PicoはGPIO25番にLEDが半田付けされている。25番にしたのはこれに合わせるためである。他のピンでもいい。GPIOに1か0を印加するだけならこのSIOを使うみたいである。Single-cycleはCPUから操作したときに1クロックでその操作が完了するという意味らしい(本当か)。SIOの詳しい説明はデータシートの「2.3.1 SIO」にある。</p>
<p>
GPIO25番の機能を選択するにはIO_BANK0_BASE(<code>0x40014000</code>)から<code>0xcc</code>番目のGPIO25_CTRLレジスタの下位5ビットに、該当する機能の番号を書き込めばいい。データシートの「2.19.2 Function Select」にある表を見ると、GPIO25番のSIOは5である:</p>
-<pre><code>// set gpio functions
+<pre><code> // set gpio functions
ldr r3, io_bank0_base
mov r0, #5 // sio
mov r1, #0xcc
@@ -570,8 +570,7 @@ io_bank0_base:
<p>
GPIO25番がSIOになったので、次にこのピンからの出力を有効化する。既定値では出力は無効になっている。ハイインピーダンスってことなのかな?出力を有効にするには、SIO_BASE(<code>0xd0000000</code>)から<code>0x24</code>バイト目のSIO: GPIO_OEレジスタの該当するビット(25番のピンなので25番ビット)を<code>0x1</code>にする:
</p>
-<pre><code>
- // enable gpio output
+<pre><code> // enable gpio output
ldr r3, sio_base
mov r0, #1
lsl r0, r0, #25 // gpio25
@@ -596,7 +595,7 @@ although some individual registers (e.g. GPIO) have set/clear/xor aliases.
<p>
と書かれている。そのかわりここにも書かれている通り、SIOの一部のレジスタにはアトミックなセット/クリア/XORをするためのレジスタが用意されている。ここではLEDを点滅させるためにGPIOの出力をトグルしたいのでXOR用のレジスタを使う。SIO_BASE(<code>0xd0000000</code>)から<code>0x1c</code>バイト目のSIO: GPIO_OUT_XORレジスタがそれである。このレジスタの25番ビットに<code>0x1</code>を書き込めばいい。出力をトグルした後は少し間をおいて同じことを繰り返す。間をおくためにここでは適当な数値を1づつ減らしていって0になったら返る関数<code>delay</code>を作った。タイマーと割り込みを使ったほうが消費電力等で優位なようだが、面倒なのでとりあえずこれで:</p>
-<pre><code>// blink led on gpio25
+<pre><code> // blink led on gpio25
ldr r4, sio_base
mov r5, r0 // r0 = 1 << 25
loop:
@@ -645,7 +644,7 @@ SECTIONS
<p>
以上のソースコードは以下のように配置している:
</p>
-<pre><code>rp2040
+<pre>rp2040
├── ex1
│ ├── Makefile
│ ├── boot2.s
@@ -655,7 +654,7 @@ SECTIONS
├── Makefile
├── bin2uf2.c
└── bincrc.c
-</code></pre>
+</pre>
<p>
toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。ex1ディレクトリのMakefileは以下の通り:
</p>
diff --git a/pub/rss.xml b/pub/rss.xml
@@ -5,3117 +5,3116 @@
<description>ウェブページの更新履歴</description>
<language>ja-jp</language>
<link>https://www.mtkn.jp</link>
-<lastBuildDate>Fri, 9 Feb 2024 15:27:51 +0900</lastBuildDate>
-<pubDate>Fri, 9 Feb 2024 15:27:51 +0900</pubDate>
+<lastBuildDate>Sat, 10 Feb 2024 09:18:54 +0900</lastBuildDate>
+<pubDate>Sat, 10 Feb 2024 09:18:54 +0900</pubDate>
<docs>https://www.rssboard.org/rss-specification</docs>
<item>
-<title>味噌</title>
-<link>https://www.mtkn.jp/kitchen/recipe/miso.html</link>
-<guid>https://www.mtkn.jp/kitchen/recipe/miso.html</guid>
-<pubDate>Fri, 9 Feb 2024 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>味噌</h1>
-<time>2024-02-09</time>
-
-<h2>材料</h2>
-<p>二升の甕4つ分弱</p>
-<ul>
-<li>大豆 3.6kg</li>
-<li>米麹 5kg</li>
-<li>塩 1.8kg</li>
-<li>酒粕 500g</li>
-</ul>
-
-<h2>手順</h2>
-<ol>
-<li>大豆を24時間水に浸す</li>
-<li>麹と塩を混ぜる</li>
-<li>大豆を3時間蒸す</li>
-<li>大豆を潰す</li>
-<li>大豆を麹と混ぜる(蒸したときの汁を混ぜて硬さを調整する)</li>
-<li>甕に詰める</li>
-<li>酒粕で蓋をしてさらにラップで落し蓋をして密閉する。</li>
-<li>半年以上寝かす</li>
-</ol>
+<title>RP2040 SDKなしでLチカ</title>
+<link>https://www.mtkn.jp/computer/rp2040_1.html</link>
+<guid>https://www.mtkn.jp/computer/rp2040_1.html</guid>
+<pubDate>Sat, 10 Feb 2024 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>RP2040 SDKなしでLチカ</h1>
+<time>2023-04-25</time>
-<h2>ひとこと</h2>
+<h2>はじめに</h2>
<p>
-豆はジップロックに入れて拳に体重を乗せて押しつぶすと楽だった。ジップロックあんまり使いたくないけど。ラップで蓋をするのは好きではないので、試験的にひと瓷だけラップなしで放置することにした。酒粕も味噌っぽくなって食べられるが、風味は少し変わる。味噌汁が粕汁っぽくなる。</p>
-]]></description>
-</item>
-<item>
-<title>自己紹介</title>
-<link>https://www.mtkn.jp/about.html</link>
-<guid>https://www.mtkn.jp/about.html</guid>
-<pubDate>Tue, 23 Jan 2024 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>自己紹介</h1>
-<time>2022-07-28</time>
-
-<p><img class="portrait" src="/pics/icon.jpeg" alt="似顔絵" width="100"></p>
+パタヘネのRISC-V<sup>[1]</sup>版を買って一通り読んだらアセンブリ言語で組込のプログラミングがしたくなった。RISC-Vのマイコンボードが欲しかったのだが、安くていい感じのものが見付からなかった。代わりに秋月電子通商でArmのものがあった。RP2040マイコンボードキット<sup>[2]</sup>というものである。ウェブ上の情報も多く、データシート<sup>[3]</sup>もしっかりしていそうなので、とりあえずこれを買ってみた。</p>
+<p>
+一般的にはSDK<sup>[4]</sup>をダウンロードしてあらかじめ用意されたライブラリを使って開発するようだが、これはビルドシステムとしてcmakeというのを使っている。これがOpenBSDでは何かエラーがでて動かなかった。僕はこういう便利ツールが嫌いだ。どうせ使わんからいいんやけど。関係ないけど途中から開発環境がLinuxに替わった。SDKには便利な関数がたくさん用意されているので楽である。ハードウェアの面倒な部分がプログラマから見えないようにしているからである。しかし今回はその面倒な部分に触れてみたくて買ったので、SDKを使うと意味がない。</p>
+<p>
+ということでSDKなしで開発してみる。とりあえず定番のLチカをば。</p>
+<p>
+ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>
+</p>
+<h2>動作環境</h2>
<ul>
- <li>名前: 松田 健嗣</li>
- <li>性別: 男</li>
- <li>年齢: 20代</li>
- <li>出身: 和歌山</li>
+<li>Arch Linux 6.2.12-arch1-1
+ <ul>
+ <li>arm-none-eabi-binutils 2.40-1</li>
+ <li>GNU Make 4.4.1</li>
+ </ul>
+</li>
+<li>OpenBSD 7.3
+ <ul>
+ <li>arm-none-eabi-binutils 2.31.1</li>
+ <li>make (バージョン?)</li>
+ </ul>
+※<code>make flash</code>は動かん。<code>dmesg</code>でデバイス確認して手動でマウントする必要がある。
+</li>
</ul>
+<h2>Boot Process</h2>
<p>
-高校のころ物理学や数学が好きで、学者を志して大学に行きましたが、能力が足りずに挫折しました。当時は自分の状況を省みることなくひたすら努力していたつもりです。ところがこの努力こそが無駄の根源だったように思います。二回の留年を経て三年目には定員を割っていた生物系に進み、クモの採集にいそしんでいました。京都の山々に分け入っては標本を集める日々です。これはなかなか楽しかった。結局大学ではクモの新種を1つ記載して卒業となりました。
+RP2040は電源を入れるといくつかの段階(ここでは関係ないので省略。データシート「2.8.1 Processor Controlled Boot Sequence」に詳しく書いてある)を踏んだあと、外部のフラッシュROMの先頭から256バイトを内部のSRAMにコピーして、フラッシュにプログラムが書き込まれているかどうか確認する。RP2040はフラッシュの先頭252バイトから計算したCRC32チェックサムを、直後の253バイト目から256バイトに記録することになっている。起動時にこのチェックサムを確認することで、フラッシュにプログラムが書き込まれているかどうか確かめている。コピーした最後の4バイトと起動時に最初の252バイトから計算したチェックサムが一致していれば、そのままコピーしてきた256バイトの先頭にPCをセットして実行を開始する。一致しなければUSBデバイスモードに切り替わり、パソコンに接続するとストレージとして認識される。このストレージにUF2という形式に変換したプログラムをコピーするとプログラムがフラッシュROMやSRAMに書き込まれる。
</p>
<p>
-勉強以外では合気道部に所属していましたがこちらではなにも努力していた自覚もなく、そこそこの実力を付けていたように思います。自分にとって稽古することが自然なことだったのでしょう。特にしんどいと思ったこともなかったです。無駄な力を抜き全身をうまく使うことを合気道の稽古から学んでいたはずなのに、それが勉強の方には生かせていなかったのです。
+以上のことから、プログラムを実行するためにはCRC32を計算し、UF2という形式に変換することが必要である。ソースコードからの流れは以下の通り:
</p>
+<pre>source bin bin with
+code ----------> object ------> elf --------> bin -------> with --------> crc32 in
+ crc32 uf2 format
+ assemble link objcopy bincrc bin2uf2
+</pre>
+
+<h2>CRC(巡回冗長検査)</h2>
<p>
-在学中は社会に出て仕事をすることなど考えていなかったため、いざ就職活動となると業界を選ぶことも大変でした。かろうじて興味のあったのがコンピュータなので、それ関係の会社を覗いたりしましたが、本当に自分にあっているのか分からず一から考え直すことに。結局実家の会社を嗣ぐために同業大手に応募、採用されました。
-</p>
+入力のデータをごにょごにょしてある値を出力する。</p>
+<blockquote cite="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB">
<p>
-ところがこの会社があまりにも合わず半年で適応障害になって辞めることに。以来実家に戻って畑をするかたわら寺に通っています。この間生き方についていろいろ考えた結果、社会にもまれて気付いたら定年しているより、もっと人間らしい老い方をしたいと思うに至ったのです。常識に縛られても結局その常識に裏切られるだけです。金銭的な豊かさはあまりにも脆いものでしょう。
+データ転送等に伴う偶発的な誤りの検査によく使われている<sup>[5]</sup>。
</p>
+</blockquote>
<p>
-このサイトは日々考えていることを記しておきたいと思い立ち上げました。周りの人にはあまり共感されない僕の考えですが、なにかの参考になれば幸いです。
+らしい。
</p>
-
-<h2>著作権について</h2>
-<p>
-このウェブサイト上のコンテンツは、特に断りのない限り<a href="http://creativecommons.org/publicdomain/zero/1.0?ref=chooser-v1" rel="license noopener noreferrer">CC0 1.0</a>で公開します。</p>
-]]></description>
-</item>
-<item>
-<title>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</title>
-<link>https://www.mtkn.jp/computer/mailserver.html</link>
-<guid>https://www.mtkn.jp/computer/mailserver.html</guid>
-<pubDate>Fri, 19 Jan 2024 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</h1>
-<time>2024-01-19</time>
-
-<h2>はじめに</h2>
<p>
-少し前にさくらのVPSで独自ドメインのメールサーバーを構築した。当時はドキュメントを追うのに必死で構築手順を整理できていなかった。しかしスマホを使わない僕の数少い連絡手段なので、受信できなくなると困る。そこであらためてサーバーを構築し、その手順を確認することにした。あいかわらずDNSの知識は自信がない。セキュリティは自己責任で。</p>
-
-<h2>使うもの</h2>
-<ul>
-<li>VPS: さくらのVPS (IPアドレスを<i>sss.sss.sss.sss</i>とする)</li>
-<li>OS: OpenBSD 7.4</li>
-<li>ドメイン: ムームードメイン (mtkn.jpとする)</li>
-<li>SMTPサーバー(MTA): OpenSMTPD 7.4.0</li>
-<li>IMAPサーバー(MDA): Dovecot 2.3.20</li>
-</ul>
-
-<h2>メールの仕組み</h2>
+入力のビットを一列に並べて、除数で「割り算」していく。この「割り算」が多項式の除算に似ているので、この除数をCRC多項式というらしい。ただし多項式の除算と違い、引き算するところをXORする。CRC32の場合、除数は33ビットである。33ビットで割ると32ビットの余りが残る。この余りがCRC32のチェックサムである。除数は色々あるようだが、標準的なものがWikipedia<sup>[5]</sup>に列挙されている。除数<code>1011</code>を使ったCRC3の計算の手順は以下の通り:
+</p>
+<pre><code>1110101011011100110101101101111 入力(適当)
+1011 除数(4ビット)
+-------------------------------
+ 101101011011100110101101101111 結果(入力と除数のXOR)
+ 1011
+ ------------------------------
+ 00001011011100110101101101111
+ 1011
+ -------------------------
+ 000011100110101101101111
+ 1011
+ --------------------
+ 1010110101101101111
+ 1011
+ -------------------
+ 001110101101101111
+ 1011
+ ----------------
+ 101101101101111
+ 1011
+ ---------------
+ 00001101101111
+ 1011
+ ----------
+ 110101111
+ 1011
+ ---------
+ 11001111
+ 1011
+ --------
+ 1111111
+ 1011
+ -------
+ 100111
+ 1011
+ ------
+ 01011
+ 1011
+ ----
+ 000 CRC3チェックサム
+</code></pre>
<p>
-メールの送受信にはSMTPというプロトコルが使われ、この通信を担うのがSMTPサーバーと呼ばれるソフトウェアである。このサーバーはメールの転送を担うので、MTA(Mail Transfer Agent)とも呼ばれる。今回はMTAとしてOpenSMTPDというのを使う。手元のパソコン等で書いたメールは、SMTPの通信で送信側のMTAに送られ、そこから受信側のMTAに転送される。受信側のMTAでは届いたメールをユーザーごとにふりわけ、所定の場所に保存する。</p>
+普通の割り算と基本は同じであるが、引き算の部分だけXORになっている。</p>
<p>
-受信側のユーザーは、手元のパソコン等から受信側のサーバーに繋ぎ、自分宛てのメールを確認する。この時に使われるのがIMAPというプロトコルである(POPというのもあるが今回は割愛)。IMAPによるこの通信を担うのがIMAPサーバーで、メールを配達する役割を担うことから、MDA(Mail Delivery Agent)とも呼ばれる。今回MDAとしてDovecotを使用する。</p>
+以上の計算をプログラムの先頭252バイトに対して、33ビットの除数を用いて行う。データの並べ方は、上の例において左側を先頭に、フラッシュROM上の0番地から、各バイトは最上位ビットから順に並べる。入力のデータは253バイト目から256バイト目に<code>0</code>をひっつけて計算する。これは多分予め長さが分からないデータでも計算できるようにしたかったからかな。除数は<code>0x104c11db7</code>である(最上位ビットは常に1なのでデータシートでは省略されている)。</p>
<p>
-以上をまとめると以下のようになる:
+入力データは1バイトづつ処理したいみたいである。多分通信等で使う都合である。この時XORは結合則が成り立つので1バイト処理した結果と次のバイトとをXORして次の処理の入力として利用することができる:
</p>
-<pre>
-+------------+ +----------------+
-|MTA(mtkn.jp)|--SMTP->| MTA |
-+------------+ +--example.com---+
- ^ | MDA |
- | +----------------+
- | |
- SMTP IMAP
- | |
- | v
-+------------+ +----------------+
-|user@mtkn.jp| |user@example.com|
-+------------+ +----------------+
-
-</pre>
-<ol>
-<li>メールを書く。</li>
-<li>送信側のMTAに送る。</li>
-<li>受信側のMTAに転送する。</li>
-<li>受信者が受信側のMDAに新着メールの有無を確認する。</li>
-</ol>
-
-<p>
-SMTPサーバーどうしが通信する際、スパムメールをフィルタリングする為、いくつかの方法で送信元の本人確認を行う。その際第三者としてDNS及び、サーバーのIPアドレスを管理している人(今回の場合はさくらインターネット)が登場する。DNSにはドメインをIPアドレスにひもづける情報が登録できる。またIPアドレスの管理人には、自分が借りているIPアドレスを自分のドメインにひもづける情報を登録してもらう。これらによって、メールの送信者がドメイン、IPアドレス双方から確認できるので、なりすましを防止できる仕組みである。</p>
-
+<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
+|......|
+111000110000000000000000000000000 先頭1バイト
+100000100110000010001110110110111 除数
+------------------------------------------------------------------------
+011000010110000010001110110110111
+ 100000100110000010001110110110111
+ -----------------------------------------------------------------------
+ 010000001010000110010011011011001
+ 100000100110000010001110110110111
+ ----------------------------------------------------------------------
+ 000000110010001110101000000000101
+|......|
+ 110010001110101000000000101000000 1バイト目の結果
+ |......|
+ 10000001 入力の2バイト目
+ ----------------------------------------------------------------
+ 010010011110101000000000101000000 1バイト目の結果と2バイト目のXOR
+ 100000100110000010001110110110111 除数
+ ----------------------------------------------------------------
+ 000100011011010010001111100110111
+ .
+ .
+ .
+</code></pre>
<p>
-最近はgmail等の大手がスパム対策を強化する一環として、以下に述べるような本人確認をすべて通らなければ受信しないようにしているらしい。</p>
-
-<h2>MTAどうしのやりとり</h2>
+以上の操作は以下のようなアルゴリズムのループで実装できる。</p>
+<ul>
+<li>前回の結果と、入力データの次のバイトをXOR</li>
+<li>
+ <ul>
+ <li>先頭の1ビットが1の場合、除数とXORを取り左シフト</li>
+ <li>先頭の1ビットが0の場合、そのまま左シフト</li>
+ </ul>
+</li>
+</ul>
<p>
-以下、MTAどうしの通信内容をざっくり説明する。
+これを<code>for</code>ループで回す都合上、最初のバイトもXORを取る。上の例では最初は<code>0x0</code>とXORを取っているが、この値を<code>0x0</code>以外にすることもできる。そうした方がいろいろいいこともあるらしい。RP2040では<code>0xffffffff</code>を使う。更にこの工程を32ビットの<code>int</code>だけで行うことを考える:
</p>
+<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
-<h3>Aレコード、MXレコードによる送信先の特定</h3>
-<p>
-MTAがメールを送信する際、まずは宛先のサーバーを特定する必要がある。この際に使用するのがDNSに登録されているMXレコードである。送信元のMTAはまずDNSに宛先のドメインのMXレコードを問い合わせる。するとメールの送信先のドメイン名が分かるので、続いてこの送信先のIPアドレスを問い合わせる:</p>
-<pre>
-MTA (mtkn.jp <i>sss.sss.sss.sss</i>) DNS
-
- example.com宛のメールって
- 誰に送ったらええん?
- ---------------------------------------->
-
- mai.example.comやで(MXレコード)
- <----------------------------------------
-
- mail.example.comってどこや?
- ---------------------------------------->
-
- <i>ddd.ddd.ddd.ddd</i>やで(Aレコード)
- <----------------------------------------
-</pre>
-<p>
-次にこの宛先のMTAとの通信を開始する。しかし受信側のMTA(example.com)は、mtkn.jpを名乗るサーバーが本当にmtkn.jpかどうか確認する必要がある:</p>
-<pre>
-MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
- こんちは。mtkn.jpです。
- ---------------------------------------->
-
- なりすましちゃうか確認したろ
-</pre>
-
-<h3>SPFレコードによる送信元の本人確認</h3>
-<p>
-受信側のMTAは、自分に連絡してきたサーバーが本人かどうかをDNSに問い合わせる。このときに使うのがSPFレコードである。このレコードにはドメインの所有者が、自分のドメインからメールを送信してもいいIPアドレスを指定する。受信側のMTAは送信元のIPアドレスと、このレコードに記載されているIPアドレスを比べることで、送信側のMTAがドメインの所有者に認められているMTAかどうか判断できる。</p>
-<pre>
-DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
- mtkn.jpを名乗るやつから連絡
- 来てるんやけどこいつ本物?
- <----------------------------------------
-
- IPアドレスが<i>sss.sss.sss.sss</i>
- やったら多分本物やわ。(SPFレコード)
- ---------------------------------------->
-
- よっしゃ
-</pre>
-
-<h3>PTRレコード</h3>
-<p>
-次は逆にIPアドレスからドメインを辿って本人確認する。これに使われるのがPTRレコードである。このレコードはIPアドレスの管理人(ここではさくらインターネット)でないと登録できない。しかし、IPアドレスを借りるにあたって、自分の名前やら住所やらを提供しており、PTRレコードをきちんと設定した上でスパムメールを送ると、自分の身元がバレるので、スパムの抑止に繋がる。</p>
-<pre>
-DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
- <i>sss.sss.sss.sss</i>ってIPアドレスで
- mtkn.jpってドメイン登録されてる?
- <----------------------------------------
-
- されてるで。(PTRレコード)
- ---------------------------------------->
-
- よっしゃ
-</pre>
-
-<h3>DKIM</h3>
-<p>
-送信側MTAが本人であることを証明するため、受信側に送るメールに署名する。署名は公開鍵方式で行うが、この鍵をドメインキーというようである。この署名が付いたメールをDKIM(DomainKey Identified Mail)という。送信側はあらかじめDNSにドメインキーの公開鍵を登録しておき、メール送信時に秘密鍵で署名する:</p>
-<pre>
-MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
-
- 本人確認できたわ
- おまたせ。
- <----------------------------------------
-
- userさんにメール送るで。
- 署名も付けるで。
- ---------------------------------------->
-</pre>
-<p>
-受信側はDNSから送信側のドメイン鍵の公開鍵をダウンロードし、署名を確認する:</p>
-<pre>
-DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
- mtkn.jpが署名付きでメール
- くれたんやけどこの署名本物?
- <----------------------------------------
-
- これで確認してみて(DKIM公開鍵)
- ---------------------------------------->
-
- よっしゃ
-</pre>
+11111111111111111111111111111111 0xffffffff
+11100011000000000000000000000000 先頭1バイトを24ビットシフト
+-------------------------------- XOR
+00011100111111111111111111111111
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+00111001111111111111111111111110
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+01110011111111111111111111111100
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+11100111111111111111111111111000
+先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR:
+11001111111111111111111111110000 シフト
+00000100110000010001110110110111 除数の下位32ビット
+-------------------------------- XOR
+11001011001111101110001001000111
+先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR:
+10010110011111011100010010001110 シフト
+00000100110000010001110110110111 除数の下位32ビット
+-------------------------------- XOR
+10010010101111001101100100111001
+先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR:
+00100101011110011011001001110010 シフト
+00000100110000010001110110110111 除数の下位32ビット
+-------------------------------- XOR
+00100001101110001010111111000101
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+01000011011100010101111110001010
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+10000110111000101011111100010100 1バイト目の結果
-<h3>DMARCレコード(上記の本人確認で詐欺だと発覚した場合)</h3>
+10000001 入力の2バイト目
+-------------------------------- XOR
+00000111111000101011111100010100
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+00001111110001010111111000101000
+.
+.
+.
+</code></pre>
<p>
-この他DNSにはDMARCレコードというものを登録することができる。これは、以上の本人確認により送信側がにせものであることが確認できた際、受信側にどのような行動を取ってほしいかを記述するものである。これにより例えば、自分を名乗る詐欺師がどこかにメールを送信した場合、受信したサーバーから自分宛てに通報するように頼める(この頼みが聞き入れられるかどうかは多分受信側MTAによる)。
-</p>
-<pre>
-DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
- mtkn.jpを名乗る詐欺師から
- メールきたんやけどどないしょ?
- <----------------------------------------
-
- ここ(postmaster@mtkn.jp)に通報や。
- (DMARCレコード)
- ---------------------------------------->
-
- よっしゃ
-</pre>
+これを実装したのが以下のコード:</p>
+<pre><code>uint32_t
+crc32(uint8_t *idata, size_t len)
+{
+ uint32_t pol = 0x04C11DB7;
+ uint32_t c = 0xFFFFFFFF;
+ uint32_t b;
-<h2>サーバーの設定</h2>
-<h3>VPSの契約、ドメインの取得</h3>
-<p>
-VPSとドメインを契約する。今回はVPSをさくらインターネットで、ドメインをムームードメインでそれぞれ契約した。さくらのVPSは好きなISOからOSをインストールでき、管理画面も分かりやすい(他のVPSを知らないので比較はできないが)。ムームードメインは安い。管理画面も悪くはない。以前お名前.comでも借りたことがあるが、こちらは広告のメールがやたら届いてうっとうしかった記憶がある。</p>
+ for (int i = 0; i < len; i++) {
+ b = idata[i] << 24;
+ c ^= b;
+ for (int j = 0; j < 8; j++) {
+ c = c >> 31 & 1 ? c << 1 ^ pol : c << 1;
+ }
+ }
-<h3>OpenBSDのインストール</h3>
-<p>
-さくらのVPSにOpenBSDをインストールする。さくらインターネットが用意したISOがあるので、OS再インストールの画面からそれを選べばすぐにインストールきる。でもなんとなく気持悪いのでopenbsd.orgから本家をダウンロードした。</p>
-<p>
-インストールのこまかい手順は割愛するが、ひとつだけひっかかった点があるので書いておく。インストール先のディスクをセットアップし、OSに必要なファイルをインストールメディアからディスクにコピーすると以下のエラーがでてカーネルパニックになった:
-</p>
-<pre><code>wdc_atapi_start: not ready, st = 50
-fatal protection fault in supervisor mode
-trap type 4 code 0 rip ffffffff810081a9 cs 8 rflags 10282 cr 2 23a896000 cpl 6 rsp ffff80000e9df410
-gsbase 0xffffffff81908ff0 kgsbase 0x0
-panic: trap type 4, code=0, pc=ffffffff810081a9
-syncing disks...18 18 18 18 18 18 18 18
+ return c;
+}
</code></pre>
<p>
-原因はあまり調べていないが、どうもインストールメディアの読み込みに失敗しているようで、インストールに必要なファイルをウェブ上からダウンロードする方法を選択すると問題なくインストールできた。
+<code>main()</code>関数では上の<code>crc32()</code>に、<code>idata</code>として入力となるバイナリデータの先頭を、<code>len</code>として<code>252</code>を渡してCRC32を計算させる。その後、出力先のファイルに入力元のデータをコピーしていき、253バイト目から256バイト目だけ、計算したCRC32に置き換える。入力元のこの場所にデータが書き込まれていないかどうかは確かめていない。
</p>
-<h3>ファイアーフォールの設定</h3>
-<p>
-メールの送受信等に必要なポートを開ける:</p>
-<ul>
-<li>TCP 22: ssh接続用</li>
-<li>UDP 53: DNSとの通信用</li>
-<li>TCP 25: SMTPの通信用</li>
-<li>TCP 587: メールクライアントからMTAへの通信用(メール送信時)</li>
-<li>TCP 993: メールクライアントからMDAへの通信用(メール受信時)</li>
-<li>TCP 80, 443: <code>acme-client(1)</code>によるサーバー証明書の取得用</li>
-</ul>
+<h2>UF2(USB Flashing Format)</h2>
<p>
-以上の設定を<code>/etc/pf.conf</code>に設定する:
-</p>
-<pre><code>set skip on lo
-
-block return # block stateless traffic
-pass in log proto tcp from any to any port 22 #ssh
-
-pass out proto udp from any to <i>DNSのIPアドレス</i> port 53 #DNS
-pass out proto udp from any to any port 123 #ntp
-
-pass proto icmp #ping
-pass proto tcp from any to any port { 80, 443 } #www
-pass proto tcp from any to any port { 25, 587, 993 } #mail
-</code></pre>
+Microsoftが開発したフラッシュ書き込み用のファイル形式らしい:
+<blockquote cite="https://github.com/microsoft/uf2">
<p>
-<code>pf(4)</code>の設定を反映:
+UF2 is a file format, developed by Microsoft for PXT (also known as
+Microsoft MakeCode), that is particularly suitable for flashing microcontrollers
+over MSC (Mass Storage Class; aka removable flash drive)<sup>[6]</sup>.
</p>
-<pre><code># pfctl -f /etc/pf.conf
-</code></pre>
-
-<h3>DNSの設定</h3>
-<h4>A, MX, SPF, DMARC</h4>
+</blockquote>
<p>
-DNSのレコードを設定する。ムームーDNSの場合、コントロールパネルにログインし、サイドバーからドメイン管理>ドメイン操作>ムームーDNSと進み、設定するドメインの変更ボタンをクリックすると設定画面がでるので、以下のように設定する:</p>
+このファイルに変換する上で必要な情報はGitHubのmicrosoft/uf2<sup>[6]</sup>に表として纏められている:
+<blockquote cite="https://github.com/microsoft/uf2">
<table>
-<thead>
+<thead><tr>
+<th>Offset</th><th>Size</th><th>Value</th>
+</tr></thead>
+<tbody>
<tr>
-<th>サブドメイン</th>
-<th>種別</th>
-<th>内容</th>
-<th>優先度</th>
+<td>0</td>
+<td>4</td>
+<td>First magic number, <code>0x0A324655</code> (<code>"UF2\n"</code>)</td>
</tr>
-</thead>
-<tbody>
<tr>
-<td></td>
-<td>A</td>
-<td><i>sss.sss.sss.sss</i></td>
-<td></td>
+<td>4</td>
+<td>4</td>
+<td>Second magic number, <code>0x9E5D5157</code></td>
</tr>
<tr>
-<td>mail</td>
-<td>A</td>
-<td><i>sss.sss.sss.sss</i></td>
-<td></td>
+<td>8</td>
+<td>4</td>
+<td>Flags</td>
</tr>
<tr>
-<td></td>
-<td>MX</td>
-<td>mail.mtkn.jp</td>
-<td>10</td>
+<td>12</td>
+<td>4</td>
+<td>Address in flash where the data should be written</td>
</tr>
<tr>
-<td></td>
-<td>TXT</td>
-<td>v=spf1 mx -all</td>
-<td></td>
+<td>16</td>
+<td>4</td>
+<td>Number of bytes used in data (often 256)</td>
</tr>
<tr>
-<td>_dmarc</td>
-<td>TXT</td>
-<td>v=DMARC1;p=reject;rua=mailto:postmaster@mtkn.jp</td>
-<td></td>
+<td>20</td>
+<td>4</td>
+<td>Sequential block number; starts at 0</td>
</tr>
-</tbody>
-</table>
-
-<p>
-ひとつめのAレコードは、ドメイン名(mtkn.jp)にIPアドレス(<i>sss.sss.sss.sss</i>)を紐付けるものである。ふたつめはmail.mtkn.jpのIPアドレスを登録するものである。みっつめのMXレコードは、このドメイン宛てのメール(user@mtkn.jp等)をどこのサーバーに送信するかを決めるものである。上記の設定では、mail.mtkn.jpに送信するようにしている。以上の設定で、user@mtkn.jp宛てのメールは、mail.mtkn.jpというサーバーに送信すればよいことが分かり、さらにこのサーバーのIPアドレスは<i>sss.sss.sss.sss</i>であることも分かる。
-</p>
-<p>
-次のTXTレコードはSPFレコードである。v=spf1でSPFのバージョンを示し、mxでドメインのMXレコードに登録されたIPアドレスからの送信を許可し、-allでそれ以外のIPアドレスからの送信を禁止している。
-</p>
-<p>
-最後のものはDMARCレコードである。v=DMARC1でバージョンを示し、p=rejectで、本人確認をクリアしなかったメールについて、その受信者が受信を拒否するように指定している。rua=mailto:postmaster@mtkn.jpでは、本人確認をクリアしなかったメールを受信した場合の通報先を指定している。</p>
+<tr>
+<td>24</td>
+<td>4</td>
+<td>Total number of blocks in file</td>
+</tr>
+<tr>
+<td>28</td>
+<td>4</td>
+<td>File size or board family ID or zero</td>
+</tr>
+<tr>
+<td>32</td>
+<td>476</td>
+<td>Data, padded with zeros</td>
+</tr>
+<tr>
+<td>508</td>
+<td>4</td>
+<td>Final magic number, <code>0x0AB16F30</code></td>
+</tr>
+</tbody>
+</table>
+</blockquote>
-<h4>PTR</h4>
-<p>PTRレコードを設定する。さくらのVPSの場合、VPS管理画面にログインし、ネットワーク>ホスト名逆引き登録と進み、カスタムホスト名に自分のドメイン名(mtkn.jp)を入力する。
+<p>
+RP2040のデータシート<sup>[3]</sup>「2.8.4.2 UF2 Format Details」を見ると、8バイト目のFlagsは、28バイト目にファミリーIDが書き込まれていることを示す<code>0x00002000</code>、12バイト目は、書き込みを行うフラッシュROMの先頭アドレスである<code>0x10000000</code>に、各ブロックの先頭からの位置を足したもの、16バイト目の、各ブロックのデータサイズは256バイト、28バイト目のファミリーIDは<code>0xe48bff56</code>である。あとは表の通り3つのマジックナンバーをセットし、32バイト目以降にデータを書き込み、20バイト目と24バイト目にブロックの通し番号と総数をそれぞれ書き込めばいい。ブロックの通し番号はデータのついでに書き込めるが、総数はデータを全部さばいた後でないと分からないので、最後全てのブロックにまとめて書き込むようにした。できたのが以下のコード:
</p>
+<pre><code>#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
-<h3>OpenSMTPDの設定</h3>
-<h4>ユーザーの作成</h4>
-<p>
-メールの送受信を担当するユーザーを作成する:</p>
-<pre><code># useradd -m -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail
-</code></pre>
-<h4>サーバー証明書の取得</h4>
-<p>
-<code>httpd(8)</code>の設定ファイルを作成する。<code>/etc/examples</code>からサンプルの設定ファイルをコピーして編集する。変更箇所はドメイン名だけでいい。編集後、httpdを起動する。</p>
-<pre><code># cp /etc/examples/httpd.conf /etc/
-# vi /etc/httpd.conf
-# echo httpd_flags= >> /etc/rc.conf.local
-# rcctl start httpd
-# rcctl enable httpd
-</code></pre>
+size_t
+fwrite32l(uint32_t d, FILE *f)
+{
+ int i;
+ uint8_t b;
+ for (i = 0; i < 32; i += 8) {
+ b = (uint8_t) (d >> i & 0xff);
+ fwrite(&b, 1, 1, f);
+ if (ferror(f)) {
+ fprintf(stderr, "Fwrite32l: write error.\n");
+ return 0;
+ }
+ }
+ return 4;
+}
-<p>
-続いて<code>acme-client(1)</code>を使ってLet's Encryptでサーバー証明書を発行する。まずはこちらもサンプルの設定ファイルをコピーしてドメイン名を変更する。その後、<code>acme-client(1)</code>を実行し、さらに<code>cron(8)</code>に登録することで、証明書が自動で更新されるようにする。</p>
-<pre><code># cp /etc/examples/acme-client.conf /etc/
-# vi /etc/acme-client.conf
-# acme-client -v mail.mtkn.jp
-# crontab -e
-</code></pre>
+int
+main(int argc, char *argv[])
+{
+ FILE *src = NULL, *dst = NULL;
+ size_t sdata = 476;
+ int retnum = 0;
-<pre><code>#minute hour mday month wday [flags] command
-~ 2 * * * acme-client mail.mtkn.jp && rcctl restart smtpd
-</code></pre>
+ uint32_t mag1 = 0x0A324655;
+ uint32_t mag2 = 0x9E5D5157;
+ uint32_t flags = 0x00002000; // familyID present
+ uint32_t addr = 0x10000000;
+ uint32_t nbyte = 256;
+ uint32_t blk = 0;
+ uint32_t nblk = 0;
+ uint32_t famid = 0xe48bff56;
+ uint8_t data[sdata];
+ uint32_t mag3 = 0x0AB16F30;
-<h4>ドメインキーの作成とDNSへの登録</h4>
+ memset(data, 0, sdata);
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s src dst\n", argv[0]);
+ exit(1);
+ }
+
+ if ((src = fopen(argv[1], "rb")) == NULL) {
+ fprintf(stderr, "Could not open %s.\n", argv[1]);
+ retnum = 1;
+ goto defer;
+ }
+ if ((dst = fopen(argv[2], "wb")) == NULL) {
+ fprintf(stderr, "Could not open %s.\n", argv[2]);
+ retnum = 1;
+ goto defer;
+ }
+
+ while (!feof(src)) {
+ fwrite32l(mag1, dst);
+ fwrite32l(mag2, dst);
+ fwrite32l(flags, dst);
+ fwrite32l(addr, dst);
+ fwrite32l(nbyte, dst);
+ fwrite32l(blk, dst);
+ fwrite32l(nblk, dst); // dummy
+ fwrite32l(famid, dst);
+
+ fread(data, 1, nbyte, src);
+ if (ferror(src)) {
+ fprintf(stderr, "Read error: %s.\n", argv[1]);
+ retnum = 1;
+ goto defer;
+ }
+ fwrite(data, 1, sdata, dst);
+ if (ferror(src)) {
+ fprintf(stderr, "Write error: %s.\n", argv[2]);
+ retnum = 1;
+ goto defer;
+ }
+
+ fwrite32l(mag3, dst);
+
+ addr += nbyte;
+ blk++;
+ nblk++;
+ }
+
+ for (int i = 0; i < nblk; i++) {
+ if (i == 0)
+ if (fseek(dst, 24, SEEK_SET) < 0) {
+ fprintf(stderr, "Seek error: %s.\n argv[2]");
+ retnum = 1;
+ goto defer;
+ }
+ fwrite32l(nblk, dst);
+ if (i < nblk - 1)
+ if(fseek(dst, 512 - 4, SEEK_CUR) < 0){
+ fprintf(stderr, "Seek error: %s.\n argv[2]");
+ retnum = 1;
+ goto defer;
+ }
+ }
+
+defer:
+ if (src)
+ fclose(src);
+ if (dst)
+ fclose(dst);
+ return retnum;
+}
+</code></pre>
+<p><code>fwrite32l()</code>関数は指定されたファイルに32ビットの整数を下位バイトから順に書き込む関数である。バイトオーダーとかややこしそうなので作っておいたけど必要なのかな?あと名前が気に入らない。</p>
<p>
-OpenSMTPD用のDKIMフィルターをインストールする。
+CRC32のチェックサムが書き込まれたバイナリファイルを、このプログラムでUF2に変換し、生成されたファイルをUSBストレージとして接続したRP2040にコピーすればフラッシュROMに書き込まれる。
</p>
-<pre><code># pkg_add opensmtpd-filter-dkimsign
-</code></pre>
+<h2>Flash Second Stage</h2>
<p>
-ドメインキーとしてRSAの秘密鍵と公開鍵を作成する。DNSのTXTレコードの制約により、鍵の長さは1024ビットにする。
+RP2040に電源を投入し、CRC32のチェックが通った後、フラッシュROMからコピーされたプログラムの先頭から実行が開始される。このコピーされた部分で、その後の動作に必要な各種の設定を行うことになる。RP2040のデータシートには、フラッシュROMとSSIコントローラのXIPを設定するようにと書かれている。XIPはExecute in Placeの略で、フラッシュROMの内容をCPUから直接実行するものである。SSIはSynchronous Serial Interfaceの略で、周辺機器と情報のやりとりをする通信方式である。RP2040はチップに内蔵されたこのSSIコントローラを通して、外部のフラッシュROMと通信しているのだが、このコントローラを適切に設定すればフラッシュROMの内容がCPUから直接アクセスできる<code>0x10000000</code>番地以降にマップされる。これによりフラッシュROMから内部のSRAMにデータをコピーすることなく命令を実行できるので、速くて便利だという。
</p>
-<pre><code># openssl genrsa -out /etc/mail/dkim/private.key 1024
-# openssl rsa -in /etc/mail/dkim/private.key -pubout -out /etc/mail/dkim/public.key
-# chown -r _dkimsign:_dkimsign /etc/mail/dkim
-# chmod 0400 /etc/mail/dkim/private.key
+<p>
+しかしこのSSIコントローラはSynopsysという会社のDW_apb_ssiというIPを使っているようで、データシートのSSIコントローラの章は多分Synopsysの人が書いている。その他の章はRaspberry Pi財団の書いたブリティッシュイングリッシュだが、この部分だけ多分ネイティブじゃない人の書いたいい加減な英語である。誤植も多い。何日かかけて理解しようとしたがよく分からん。不毛なので一旦諦めた。</p>
+<p>
+RP2040には内部にもROMがあり、はバージョン情報や電源を投入した時の動作、その他便利な関数が書き込まれている。この関数の中に外部のフラッシュROMとSSIコントローラを設定するものも含まれているので、今回はこれを利用した。ただしこの方法だとフラッシュROMとの通信方式がStandard SPIのままなので少し遅いらしい。詳しくはデータシートの「2.3.8. Bootrom Contents」を参照。
+</p>
+<p>
+RP2040の内蔵ROMの<code>0x00000018</code>番地に関数を検索するための関数がある。この関数に<code>0x00000014</code>番地の<code>rom_func_table</code>と、各関数に割り当てられた二文字の文字列を渡せば、欲しい関数へのポインタが返ってくる。なお、二文字の文字列はそれぞれASCIIコードで現し、二文字目を8ビットシフトしたものと1文字目のORを取ったものを渡すことになっている。今回欲しい関数はフラッシュROMをXIPに設定するもの(<code>_flash_enter_cmd_xip()</code>)なので、<code>'C', 'X'</code>を渡す。関数のポインタが返ってきて、それを呼び出せばフラッシュROMとSSIはXIPモードになる:
+</p>
+<pre><code>setup_xip:
+ ldr r3, rom_base
+
+ ldrh r0, [r3, #0x14] // rom_func_table
+ ldr r1, =('C' | 'X' << 8) // _flash_enter_cmd_xip()
+ ldrh r2, [r3, #0x18] // rom_table_lookup
+ blx r2
+ blx r0
+/* ... */
+rom_base:
+ .word 0x00000000
</code></pre>
-<p>作成したドメインキーの公開鍵をDNSに登録する:</p>
+<p>
+XIPの設定が完了すれば、次はメインのプログラムを実行するための準備である。エントリーポイントの指定、スタックポインタの初期値の設定、ベクターテーブルの設定である。Armのマニュアル<sup>[7]</sup>によると、初期スタックポインタとエントリーポイントはベクターテーブルの<code>0x0</code>バイト目と<code>0x4</code>バイト目に書くことになっている:</p>
+<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/Exception-number-definition">
<table>
-<thead>
-<tr>
-<th>サブドメイン</th>
-<th>種別</th>
-<th>内容</th>
-<th>優先度</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td>selector1._domainkey.mail</td>
-<td>TXT</td>
-<td>v=DKIM1;k=rsa;p=<i>公開鍵</i></td>
-<td></td>
-</tr>
-</tr>
-</tbody>
+<caption>
+Table 7.3. Exception numbers
+</caption><colgroup><col><col></colgroup><thead><tr><th>Exception number</th><th>Exception</th></tr></thead><tbody><tr><td>1</td><td>Reset</td></tr><tr><td>2</td><td>NMI</td></tr><tr><td>3</td><td>HardFault</td></tr><tr><td>4-10</td><td>Reserved</td></tr><tr><td>11</td><td>SVCall</td></tr><tr><td>12-13</td><td>Reserved</td></tr><tr><td>14</td><td>PendSV</td></tr><tr><td>15</td><td>SysTick, optional</td></tr><tr><td>16</td><td>External Interrupt(0)</td></tr><tr><td>...</td><td>...</td></tr><tr><td>16 + N</td><td>External Interrupt(N)</td></tr></tbody>
</table>
+</blockquote>
+<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/The-vector-table">
+<table>
+<caption>
+Table 7.4. Vector table format
+</caption><colgroup><col><col></colgroup><thead><tr><th>Word offset in table</th><th>Description, for all pointer address values</th></tr></thead><tbody><tr><td>0</td><td>SP_main. This is the reset value of the Main stack pointer.</td></tr><tr><td>Exception Number</td><td>Exception using that Exception Number</td></tr></tbody>
+</table>
+</blockquote>
<p>
-サブドメインのselector1は以下のsmtpd.confで<code>filter-dkimsign</code>の-sオプションに
-指定してものと同じものであれば他のものでもいい。</p>
-
-<h4>smtpd.confの設定</h4>
-<p>
-/etc/mail/smtpd.confを以下の通り変更:</p>
-<pre><code># 認証に使用するサーバー証明書と秘密鍵を"mtkn.jp"という名前で登録。
-# 証明書のファイルは先程acme-client(1)で取得したもの。
-pki mtkn.jp cert "/etc/ssl/mail.mtkn.jp.fullchain.pem"
-pki mtkn.jp key "/etc/ssl/private/mail.mtkn.jp.key"
+また、ベクターテーブルはメインのプログラムの先頭に置くことにする。メインのプログラムはFlash Second Stageが占有する256バイトの直後、フラッシュROMの257バイト目から配置することにする。RP2040のベクターテーブルはM0PLUS: VTOR(<code>0xe0000000 + 0xed08</code>)というレジスタに書き込むことで設定する。以上をまとめると以下のコードになる:</p>
+<pre><code> ldr r0, flash_main
+ ldr r1, m0plus_vtor
+ str r0, [r1, #0] // vector table
+ ldr r1, [r0, #4] // entry point
+ ldr r0, [r0, #0] // stack pointer
+ mov sp, r0
+ bx r1
-# テーブルの登録
-# ローカルに届いたメールの転送を設定するテーブル
-table aliases file:/etc/mail/aliases
-# ユーザーの名前と暗号化されたパスワードのテーブル
-table passwd file:/etc/mail/passwd
-# メールの転送を設定するテーブル
-table virtuals file:/etc/mail/virtuals
+/* ... */
-# フィルターの登録
-# ドメインキーによる署名を付加するフィルター
-filter dkim_sign proc-exec\ "filter-dkimsign -d mail.mtkn.jp -s selector1\ -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign
-# DNSの逆引き(PTRレコード)ができなかった場合に受信を拒否するフィルター
-filter check_rdns phase connect match !rdns disconnect "550 no rDNS."
-# 正引き(Aレコード)と逆引き(PTRレコード)でDNSのレコードが一致しなかった
-# 場合に受信を拒否するフィルター
-filter check_fcrdns phase connect match !fcrdns disconnect "550 no FCrDNS."
-
-# 各ネットワークインターフェース毎の処理を設定。
-listen on socket
-listen on lo0
-# vio0というインターフェースの25番ポートに届いたものに関する設定。
-# 外部からmtkn.jp宛に届いたものの処理である。
-# 最初に登録した証明書と秘密鍵を使ってMTAの本人確認をし、
-# 上で設定したDNSに関するフィルターを適応。
-listen on vio0 port 25 tls pki mtkn.jp hostname "mail.mtkn.jp"\ filter { check_rdns, check_fcrdns }
-# vio0というインターフェースのsubmission(587番)ポートに関する設定。
-# mtkn.jpのユーザー(e.g. user@mtkn.jp)から任意の人宛に送信される
-# メールに関する設定。
-# 上と同様に"mtkn.jp”という名前で登録されたpkiを使ってMTAの本人確認をし、
-# "passwd"という名前で登録したパスワードで認証し、
-# さらにドメインキーでメールに署名する。
-listen on vio0 mask-src port submission smtps pki mtkn.jp\ hostname "mail.mtkn.jp" auth <passwd> filter dkim_sign
-
-# 各種メールの処理を定義する。
-# 各処理が実行される条件はこの後定義する。
-# ローカルメールはデフォルトのまま
-action "local_mail" mbox alias <aliases>
-# 自分のドメイン宛のメールは/var/vmail/以下に振り分けてmaildir形式で保存。
-action "domain" maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}"\ virtual <virtuals>
-# 他のドメイン宛ては宛先へ転送。
-action "outbound" relay
-
-# どういう条件でどういう処理をするかを定義する。
-# 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。
-# ここでは/var/vmail/以下に振り分けて保存する。
-match from any for domain mtkn.jp action "domain"
-# このサーバー内でのメールはlocal_mailとして処理。
-match from local for local action "local_mail"
-# このサーバー内から他所へのメールは宛先に転送
-match from local for any action "outbound"
-# 外部から来た他所宛てのメールは認証されているものに限り転送。
-# authを忘れると多分このサーバー経由で迷惑メールが送られる危険がある。
-match auth from any for any action "outbound"
+flash_main:
+ .word 0x10000000 + 0x100
+m0plus_vtor:
+ .word 0xe0000000 + 0xed08
</code></pre>
+<p>なお以上のコードは<code>.boot2</code>という名前のセクションにしてある。
+</p>
-<h4>各テーブルの作成</h4>
+<h2>メインのコード(<code>main.s</code>)</h2>
+<h3>ベクターテーブル</h3>
<p>
-上記の設定ファイルの<code>table</code>で指定したテーブルを作成する。<code>/etc/mail/aliases</code>は特に変更しなくてもいい。<code>/etc/mail/passwd</code>にはメールアカウントと暗号化されたパスワードを保存する。パスワードの暗号化には<code>smtpctl(8)</code>を使う:</p>
-<pre><code># echo user@mtkn.jp:$(smtpctl encrypt) >> /etc/mail/passwd
-<i>パスワードを入力し、エンター、Ctrl-D</i>
+メインのコードの最初には上で説明したベクターテーブルを配置する。ここでは割り込みの処理は考えないので、初期スタックポインタとエントリーポイントだけである。初期スタックポインタはSRAMの最後?(<code>0x20040000</code>)、エントリーポイントはエントリーポイントのラベルを用いて設定した。</p>
+<pre><code>vectors:
+ .word 0x20040000 // initial SP
+ .word (reset+1)
</code></pre>
+<p>
+<code>reset</code>ラベルに<code>1</code>を足しているのはRP2040がThumbモードのみに対応しているからである。ArmのCPUはArmモードとThumbモードがあり、Armモードは32ビットの命令で高機能。Thumbモードは16ビットの命令(一部32ビット)でコンパクトである。どちらのモードでも命令は2の倍数のアドレスに並ぶことになる。そのためジャンブ命令のジャンプ先のアドレスの最下位ビットは常に0である。この最下位ビットはジャンプ先のモードを示す為に利用される。両方のモードに対応したCPUではジャンプ先のアドレスの最下位ビットが0ならArmモード、1ならThumbモードに切り替わる。ブランチ命令のオペランド等は多分アセンブラがいい感じにしてくれるので単にラベルを書けば動く。ベクターテーブルのこの部分は自分で足す必要があるみたい。あんまりちゃんと調べてないのでマニュアル読んでや。</p>
+<p>
+この部分のセクション名は<code>.vectors</code>である。
+</p>
+<h3>GPIOの設定</h3>
<p>
-<code>/etc/mail/virtuals</code>にはメールの転送を設定できる。以下のようにする:</p>
-<pre><code>abuse@mtkn.jp user@mtkn.jp
-postmaster@mtkn.jp user@mtkn.jp
-webmaster@mtkn.jp user@mtkn.jp
-user@mtkn.jp vmail
-</code></pre>
+電源投入直後、RP2040の周辺機器はリセット状態になっている。まずは今回利用するGPIOのリセット状態を解除する必要がある。データシートの「2.14. Subsystem Resets」には以下のように書かれている:
+</p>
+<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">
<p>
-この設定で、例えばabuse@mtkn.jp宛てのメールがuser@mtkn.jpに転送される。abuse, postmaster, webmasterはなんか一応作っとけみたいに書いてたけど必要なんかな?
+Every peripheral reset by the reset controller is held in reset at power-up.
+It is up to software to deassert the reset of peripherals it intends to use.
+</p>
+</blockquote>
+<p>
+リセット状態を解除するには、RESETS_BASE(<code>0x4000c000</code>)から<code>0x0</code>バイト目のRESETS: RESETレジスタのうち利用したい周辺機器のビットを<code>0x0</code>にすればいい。
+GPIOはIO Bank 0なので(これ明記されてなくない?)、RESETS: RESETレジスタのIO_BANK0(5番ビット)を<code>0x0</code>にする。
+</p>
+<h4>レジスタのアトミックなクリア</h4>
+<p>
+RESETS: RESETレジスタのうち5番ビットだけを<code>0x0</code>にしたい。この時、まずこのレジスタを読み込んでから<code>~(1 << 5)</code>と論理積を取って同レジスタに書き戻してもいいのだが、RP2040にはこれを一回の<code>str</code>でしかもアトミックにできる機能が用意されている。今回の場合アトミックかどうかは関係ないと思うけど。</p>
+<p>
+各レジスタには4個のアドレスが割り当てられている。データシートの各章のList of Registersに記載されているアドレスは通常の読み書きができる。そのアドレスに<code>0x1000</code>を足したものにアクセスするとアトミックなXORが、<code>0x2000</code>を足したものはアトミックなセットが、<code>0x3000</code>を足したものはアトミックなクリアができる。つまりレジスタのアドレスに<code>0x3000</code>を足したものに、<code>0x1 << 5</code>を<code>str</code>すれば5番目のビットだけ<code>0x0</code>にして、他のビットは変更されない。逆に指定したビットだけ立てて他を触らない場合は<code>0x2000</code>を、あるいは指定したビットだけトグルしたい場合は<code>0x1000</code>を足したアドレスにアクセスすればいい。</p>
+<h4>リセット状態の確認</h4>
+<p>リセットの解除はすぐに完了するわけではないようである。リセットの解除が完了したかどうか確認するにはRESETS: RESET_DONEレジスタ(RESETS_BASEから<code>0x8</code>バイト目)の該当するビット(ここでは5番目のビット)を読む。この値が<code>0x1</code>であればリセットの解除が完了している。<code>0x0</code>であれば処理が進行中なので<code>0x1</code>が返ってくるまで繰り返し読み込んで<code>0x0</code>になるまで待機する。ところでこのレジスタはリセットの解除が完了したかどうか確かめるものなので、RESET_DONEという名前はどうなん?
+<p>
+以上から、GPIOのリセットを解除するのは以下のコード:
</p>
+<pre><code>reset:
+ // unreset gpio
+ mov r0, #1
+ lsl r0, r0, #5 // io_bank0
+ ldr r3, resets_base
+ ldr r1, atomic_clr
+ str r0, [r3, r1] // RESETS: RESET
+reset_chk:
+ ldr r1, [r3, #0x8] // RESETS: RESET_DONE
+ tst r0, r1
+ beq reset_chk
-<h3>Dovecotの設定</h3>
+/* ... */
+
+atomic_clr:
+ .word 0x00003000
+resets_base:
+ .word 0x4000c000
+</code></pre>
+
+<h3>GPIOの機能の選択</h3>
+<p>RP2040のGPIOにはそれぞれ複数の機能が用意されていて、どれを使うかはソフトウェアから選択できる。利用できる機能の一覧と各機能の説明はデータシートの「2.19.2 Function Select」に詳しく書いてある。ここではGPIO25番のピンをSIO(Single-cycle IO)として利用する。同じCPUが載っているRaspberry Pi PicoはGPIO25番にLEDが半田付けされている。25番にしたのはこれに合わせるためである。他のピンでもいい。GPIOに1か0を印加するだけならこのSIOを使うみたいである。Single-cycleはCPUから操作したときに1クロックでその操作が完了するという意味らしい(本当か)。SIOの詳しい説明はデータシートの「2.3.1 SIO」にある。</p>
<p>
-MDAとして<code>dovecot(1)</code>をインストールする。</p>
-<pre><code># pkg_add dovecot
+GPIO25番の機能を選択するにはIO_BANK0_BASE(<code>0x40014000</code>)から<code>0xcc</code>番目のGPIO25_CTRLレジスタの下位5ビットに、該当する機能の番号を書き込めばいい。データシートの「2.19.2 Function Select」にある表を見ると、GPIO25番のSIOは5である:</p>
+<pre><code> // set gpio functions
+ ldr r3, io_bank0_base
+ mov r0, #5 // sio
+ mov r1, #0xcc
+ str r0, [r3, r1] // IO_BANK0: GPIO25_CTRL
+
+/* ... */
+
+io_bank0_base:
+ .word 0x40014000
</code></pre>
+
+<h3>GPIOの出力を有効化</h3>
<p>
-dhparamを作成する(時間がかかる):
+GPIO25番がSIOになったので、次にこのピンからの出力を有効化する。既定値では出力は無効になっている。ハイインピーダンスってことなのかな?出力を有効にするには、SIO_BASE(<code>0xd0000000</code>)から<code>0x24</code>バイト目のSIO: GPIO_OEレジスタの該当するビット(25番のピンなので25番ビット)を<code>0x1</code>にする:
</p>
-<pre><code># openssl dhparam -out /etc/ssl/dh.pem 4096
+<pre><code> // enable gpio output
+ ldr r3, sio_base
+ mov r0, #1
+ lsl r0, r0, #25 // gpio25
+ str r0, [r3, #0x24] // SIO: GPIO_OE
+
+/* ... */
+
+sio_base:
+ .word 0xd0000000
</code></pre>
+<h3>LEDの点滅</h3>
+<p>以上でGPIOの設定は完了したので、あとは実際にLEDに電圧を掛けるだけである。レジスタのアドレスに<code>0x1000</code>を足したものに書き込むとアトミックなレジスタのXORができると書いたが、SIOはこの機能がサポートされていないようである。データシートの「2.1.2 Atomic Register Access」に、
+</p>
+<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">
<p>
-わかりにくい設定ファイルをどけてシンプルなものに書き直す:
+The SIO (Section 2.3.1), a single-cycle IO block attached directly to the cores'
+IO ports, does <strong>not</strong> support atomic accesses at the bus level,
+although some individual registers (e.g. GPIO) have set/clear/xor aliases.
</p>
-<pre><code># mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
-# vi /etc/dovecot/dovecot.conf
-</code></pre>
+</blockquote>
<p>
-ドキュメントは公式ウェブサイトで確認できる。分量が多すぎて読む気にならない。サンプルの設定ファイルにも各項目の説明と既定値が書いてあるがこれも分かりにくい。パスワードが漏洩するのはとりあえず避けたいので、<code>ssl = required</code>と暗号化されていないIMAPを<code>port = 0</code>として無効にする。他はとりあえず以下の設定で動く:</p>
-<pre><code># IPv4は全て監視
-listen = *
+と書かれている。そのかわりここにも書かれている通り、SIOの一部のレジスタにはアトミックなセット/クリア/XORをするためのレジスタが用意されている。ここではLEDを点滅させるためにGPIOの出力をトグルしたいのでXOR用のレジスタを使う。SIO_BASE(<code>0xd0000000</code>)から<code>0x1c</code>バイト目のSIO: GPIO_OUT_XORレジスタがそれである。このレジスタの25番ビットに<code>0x1</code>を書き込めばいい。出力をトグルした後は少し間をおいて同じことを繰り返す。間をおくためにここでは適当な数値を1づつ減らしていって0になったら返る関数<code>delay</code>を作った。タイマーと割り込みを使ったほうが消費電力等で優位なようだが、面倒なのでとりあえずこれで:</p>
-# SSL接続のみ許可
-ssl = required
+<pre><code> // blink led on gpio25
+ ldr r4, sio_base
+ mov r5, r0 // r0 = 1 << 25
+loop:
+ str r5, [r4, #0x1c] // SIO: GPIO_OUT_XOR
+ bl delay
+ b loop
-# SSLに使用するファイル
-ssl_cert = </etc/ssl/mail.mtkn.jp.fullchain.pem
-ssl_key = </etc/ssl/private/mail.mtkn.jp.key
-ssl_dh = </etc/ssl/dh.pem
+delay:
+ mov r0, #1
+ lsl r0, r0, #20
+delay_loop:
+ sub r0, r0, #1
+ bne delay_loop
+ bx lr
-# メールの保存場所は各ユーザーのホームディレクトリ。
-# ホームディレクトリの場所は以下のuserdbで設定。
-mail_location = maildir:~
+/* ... */
-# パスワードのファイル。
-# OpenSMTPDと共有。
-passdb {
- args = scheme=BLF-CRYPT /etc/mail/passwd
- driver = passwd-file
-}
+sio_base:
+ .word 0xd0000000
+</code></pre>
+<p>なお以上のコードは<code>.text</code>セクションである。</p>
-# ユーザーの設定。
-# ホームディレクトリは/var/vmail/<i>ドメイン</i>/<i>ユーザー名</i>
-userdb {
- args = uid=vmail gid=vmail home=/var/vmail/%d/%n
- driver = static
+<h2>リンカスクリプト</h2>
+<p>
+以上のコードには<code>.boot2</code>、<code>.vectors</code>、<code>.text</code>の3つのセクションが含まれる。<code>.boot2</code>はフラッシュの先頭から256(<code>0x100</code>)バイト目まで、<code>.vectors</code>と<code>.text</code>はその後ろに続くように配置する:
+<pre><code>MEMORY
+{
+ FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
}
-# 使用するプロトコル。
-protocols = imap lmtp
+SECTIONS
+{
+ .boot2 : {
+ *(.boot2)
+ . = 0x100;
+ } > FLASH
-# SSLを使ったIMAPSのみを許可。
-service imap-login {
- inet_listener imaps {
- port = 993
- ssl = yes
- }
- # Disable imap
- inet_listener imap {
- port = 0
- }
+ .text : {
+ *(.vectors)
+ *(.text)
+ } > FLASH
}
</code></pre>
-<pre><code># rcctl start dovecot
-# rcctl enable dovecot
-</code></pre>
+<h2>Makefile</h2>
<p>
-サーバーの証明書が更新される際に<code>dovecot(1)</code>も再読み込みされるように<code>cron(8)</code>に追加しておく:
+以上のソースコードは以下のように配置している:
</p>
-<pre><code>crontab -e
-</code></pre>
-
-<pre><code>#minute hour mday month wday [flags] command
-~ 2 * * * acme-client mail.mtkn.jp && rcctl restart smtpd && rcctl reload dovecot
-</code></pre>
-
-<h3>テスト</h3>
+<pre>rp2040
+├── ex1
+│ ├── Makefile
+│ ├── boot2.s
+│ ├── main.s
+│ └── memmap.ld
+└── tools
+ ├── Makefile
+ ├── bin2uf2.c
+ └── bincrc.c
+</pre>
<p>
-手元のメールクライアントから送受信のテストをする。特にgmail等に送った際に迷惑メールに分類されないかどうか確認する。以上の設定で、gmailへの送受信は問題なかった。また、<code>neomutt(1)</code>、<code>thunderbird(1)</code>からログイン及び送受信できることも確認した。</p>
+toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。ex1ディレクトリのMakefileは以下の通り:
+</p>
+<pre><code>AS = arm-none-eabi-as
+LD = arm-none-eabi-ld
+OBJCOPY = arm-none-eabi-objcopy
+BINCRC = ../tools/bincrc
+BIN2UF2 = ../tools/bin2uf2
-<h2>参考文献</h2>
-<ul>
-<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-1-what-is-email.html">Arch Linux mail server tutorial - part 1 - What is email?.unixsheikh.com</a></li>
-<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-2-opensmtpd-dovecot-dkimproxy-and-lets-encrypt.html">Arch Linux mail server tutorial - part 2 - OpenSMTPD, Dovecot, DKIMproxy, and Let's Encrypt.unixsheikh.com</a></li>
-<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-3-get-dns-right-it-is-important.html">Arch Linux mail server tutorial - part 3 - Get DNS right, it's important!.unixsheikh.com</a></li>
-<li><a href="https://man.openbsd.org/smtpd">smtpd(8).OpenBSD Manual Pages</a></li>
-<li><a href="https://man.openbsd.org/smtpd.conf">smtpd.conf(5).OpenBSD Manual Pages</a></li>
-<li><a href="https://man.openbsd.org/httpd">httpd(8).OpenBSD Manual Pages</a></li>
-<li><a href="https://man.openbsd.org/httpd.conf">httpd.conf(5).OpenBSD Manual Pages</a></li>
-<li><a href="https://man.openbsd.org/acme-client">acme-client(1).OpenBSD Manual Pages</a></li>
-<li><a href="https://man.openbsd.org/acme-client.conf">acme-client.conf(5).OpenBSD Manual Pages</a></li>
-<li><a href="https://doc.dovecot.org/">Dovecot manual — Dovecot documentation</a></li>
-</ul>
-]]></description>
-</item>
-<item>
-<title>使用しているハードウェア、ソフトウェア</title>
-<link>https://www.mtkn.jp/computer/what-i-use.html</link>
-<guid>https://www.mtkn.jp/computer/what-i-use.html</guid>
-<pubDate>Sat, 13 Jan 2024 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>使用しているハードウェア、ソフトウェア</h1>
-<time>2024-01-11</time>更新<br>
-<time>2021-12-13</time>作成
-<h2>ノートパソコン</h2>
-<p>メインのパソコン。</p>
-<dl>
-<dt>ハードウェア: Macbook Air 2013</dt>
-<dd>ThinkPad辞めた。ファンがあまりにもうるさい。ThinkPadの前に使ってたMacbook Airを引っ張り出してバッテリー交換して使ってみたらファンがめっちゃ静か。多分ほぼ回ってない。M1 Macbook Airが欲しくなってきた。</dd>
-<dt>OS: Arch Linux</dt>
-<dd>本当はOpenBSDがいいんやけどMacbook AirだとWi-Fiも使えやんしいろいろサポートが悪いので...</dd>
+MCPU = -mcpu=cortex-m0plus
+ASFLAGS = $(MCPU)
+CFLAGS = $(MCPU) -ffreestanding -nostartfiles -O0 -fpic -mthumb -c
+LDFLAGS = --no-relax -nostdlib
-<dt>Window Manager: <a href="https://git.mtkn.jp/dwm">dwm</a></dt>
+all: tools led.uf2
-<dt>Terminal: <a href="https://git.mtkn.jp/st">st</a></dt>
+clean:
+ rm -f *.o *.elf *.uf2 *.bin
+ cd ../tools && make clean
-<dt>Text Editor: acme, nvi</dt>
-<dd>vimは辞めた。OpenBSDにあったnviが丁度いい。日本語を使うのでUTF8に対応したnvi2を使っている。Arch Linuxだとちょっと使い勝手が違う。コマンド履歴が編集できないのが不便である。日本語入力が必要ない場面ではacmeを使っている。</dd>
+.s.o:
+ $(AS) $(ASFLAGS) -o $@ $<
-<dt>Browser: firefox</dt>
-<dd>もうちょっとましなのないんかな。UIころころ変わるし、重いし、嫌い。JavaScriptがないと困るので仕方なく使っているが...特に最近画面上部のUIが大きすぎて邪魔である。いまだに720pの液晶なのも悪いけど...このパソコンで上部にナビゲーションバーが固定されているページを見ると画面の半分近くがかくれてしまう...
-</dd>
+led.elf: boot2.o main.o memmap.ld
+ $(LD) $(LDFLAGS) -o $@ -T memmap.ld boot2.o main.o
-<dt>Mail Client: neomutt</dt>
-<dd></dd>
-</dl>
+led.bin: led.elf
+ $(OBJCOPY) -O binary led.elf $@
-<h2>デスクトップ</h2>
-<p>学生のときに組んだ。無駄にハイスペック。この上RTX2060も積んでたが、OpenBSDで使えないので解雇した。最近は絵を描くのに使っている。</p>
-<dl>
-<dt>マザーボード: ASRock Z390 Pro4</dt>
-<dt>CPU: Intel Core i7-9700K</dt>
-<dt>メモリ: センチュリーマイクロ 8GB * 2</dt>
-<dd>国産</dd>
-<dt>ストレージ: KimMiDi SSD 256GB</dt>
-<dd>ThinkPad買ったときについてきたやつ。</dd>
-<dt>モニタ: Eizo FlexScan EV2451</dt>
-<dd>綺麗やけど質量が大きい。</dd>
-<dt>OS: MX Linux</dt>
-<dd>Arch Linuxを使っていたが、GIMPでペンタブの筆圧感知を有効にすると落ちるので変更した。無難に使える。</dd>
-<dt>ソフト: GIMP<dt>
-<dd></dd>
-<dt>ペンタブ: wacom intuos comic CTH-480/S3</dt>
-<dd>小さいが十分使える</dd>
-</dl>
+led.uf2: led.bin
+ $(BINCRC) led.bin led_crc.bin
+ $(BIN2UF2) led_crc.bin $@
+
+flash: all
+ mount /dev/disk/by-label/RPI-RP2 /mnt
+ cp led.uf2 /mnt
+
+tools:
+ cd ../tools && make
+</code></pre>
-<h2>家のサーバー</h2>
<p>
-バックアップ用。ノートパソコンのホームディレクトリのバックアップとGIMPで描いた絵を保存するのにも使っている。GIMPの絵は自作の9Pライブラリを使ってアクセスできるようにしている。
-</p>
-<dl>
-<dt>ハードウェア: Dell D520</dt>
-<dd>おじいちゃんの家にころがってたのをもらってきた。</dd>
-<dt>OS: OpenBSD</dt>
-<dd></dd>
-</dl>
+RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し、ex1ディレクトリで</p>
+<pre><code>$ make
+# make flash
+</code></pre>
+<p>
+とすればプログラムがRP2040のボードに書き込まれて実行が開始される。</p>
-<h2>ウェブサーバー</h2>
-<p>このウェブページ用</p>
-<dl>
-<dt>VPS: さくらのVPS</dt>
-<dd>ちょっと高い(7078円/12ヶ月)。OpenBSDを入れられる。</dd>
-<dt>OS: OpenBSD</dt>
-<dt>httpサーバー: Openbsd httpd</dt>
-<dd>manが分かりやすい。</dd>
-<dt>smtpサーバー: OpenSMTPD</dt>
-<dd>manが分かりやすい。</dd>
-<dt>imapサーバー: dovecot</dt>
-<dd>manはあんまり分かり易くない。<dd>
-</dl>
+<h2>最後に</h2>
+<p>
+光あれ。
+</p>
-<h2>理想</h2>
-<dl>
-<dt>CPU: RISC-V</dt>
-<dd>はやく家庭用コンピュータの世界に来てほしい</dd>
-<dt>ファン: レス</dt>
-<dd>静音性は優先度がかなり高い。</dd>
-<dt>メモリ: 8GB</dt>
-<dd>そんないらん</dd>
-<dt>モニター: 60fpsでるカラーの電子ペーパー</dt>
-<dt>キーボード: PFU製</dt>
-<dd>macbook airに戻っても特にストレスないからキーストロークはそんなにいらんかな</dd>
-<dt>バッテリー: 24時間くらい</dt>
-<dt>OS: Plan9かOpenBSD</dt>
-<dd>ハードウェアは全部ドキュメント公開してくれ</dd>
-</dl>
+<h2>参考</h2>
+<ul>
+<li>
+[1] Hennesy, J. L. and Patterson, D. A. 2017. Computer Organization And Design RISC-V Edition.
+</li>
+<li>
+[2] <a href="https://akizukidenshi.com/catalog/g/gK-17542/">RP2040マイコンボードキット.秋月電子通商</a>
+</li>
+<li>
+[3] <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a>
+</li>
+<li>
+[4] <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a>
+</li>
+<li>
+[5] <a href="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB">巡回冗長検査.Wikipedia</a>
+</li>
+<li>
+[6] <a href="https://github.com/microsoft/uf2">USB Flashing Format (UF2).GitHub</a>
+</li>
+<li>
+[7] <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a>
+</li>
+</ul>
]]></description>
</item>
<item>
-<title>謹賀新年</title>
-<link>https://www.mtkn.jp/gallery/20240101.html</link>
-<guid>https://www.mtkn.jp/gallery/20240101.html</guid>
-<pubDate>Mon, 1 Jan 2024 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>謹賀新年</h1>
-<time>2024-01-01</time>
-<img src="img/20240101.jpg">
+<title>味噌</title>
+<link>https://www.mtkn.jp/kitchen/recipe/miso.html</link>
+<guid>https://www.mtkn.jp/kitchen/recipe/miso.html</guid>
+<pubDate>Fri, 9 Feb 2024 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>味噌</h1>
+<time>2024-02-09</time>
+
+<h2>材料</h2>
+<p>二升の甕4つ分弱</p>
+<ul>
+<li>大豆 3.6kg</li>
+<li>米麹 5kg</li>
+<li>塩 1.8kg</li>
+<li>酒粕 500g</li>
+</ul>
+
+<h2>手順</h2>
+<ol>
+<li>大豆を24時間水に浸す</li>
+<li>麹と塩を混ぜる</li>
+<li>大豆を3時間蒸す</li>
+<li>大豆を潰す</li>
+<li>大豆を麹と混ぜる(蒸したときの汁を混ぜて硬さを調整する)</li>
+<li>甕に詰める</li>
+<li>酒粕で蓋をしてさらにラップで落し蓋をして密閉する。</li>
+<li>半年以上寝かす</li>
+</ol>
+
+<h2>ひとこと</h2>
+<p>
+豆はジップロックに入れて拳に体重を乗せて押しつぶすと楽だった。ジップロックあんまり使いたくないけど。ラップで蓋をするのは好きではないので、試験的にひと瓷だけラップなしで放置することにした。酒粕も味噌っぽくなって食べられるが、風味は少し変わる。味噌汁が粕汁っぽくなる。</p>
]]></description>
</item>
<item>
-<title>Holy Shit!</title>
-<link>https://www.mtkn.jp/journal/posts/20221228.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20221228.html</guid>
-<pubDate>Thu, 28 Dec 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Holy Shit!</h1>
-<p>最近見ているプログラマの配信者がよく言っている。「Holy Shit!」Holyは聖なる、Shitはうんこという意味である。日本のプログラマもうんこという言葉を好むが、海外でも同じようである。
+<title>自己紹介</title>
+<link>https://www.mtkn.jp/about.html</link>
+<guid>https://www.mtkn.jp/about.html</guid>
+<pubDate>Tue, 23 Jan 2024 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>自己紹介</h1>
+<time>2022-07-28</time>
+
+<p><img class="portrait" src="/pics/icon.jpeg" alt="似顔絵" width="100"></p>
+
+<ul>
+ <li>名前: 松田 健嗣</li>
+ <li>性別: 男</li>
+ <li>年齢: 20代</li>
+ <li>出身: 和歌山</li>
+</ul>
+
+<p>
+高校のころ物理学や数学が好きで、学者を志して大学に行きましたが、能力が足りずに挫折しました。当時は自分の状況を省みることなくひたすら努力していたつもりです。ところがこの努力こそが無駄の根源だったように思います。二回の留年を経て三年目には定員を割っていた生物系に進み、クモの採集にいそしんでいました。京都の山々に分け入っては標本を集める日々です。これはなかなか楽しかった。結局大学ではクモの新種を1つ記載して卒業となりました。
</p>
<p>
-ところでこの言葉の訳語はなにがいいかと考えていると思いがけずぴったりなものを思いついた:</p>
+勉強以外では合気道部に所属していましたがこちらではなにも努力していた自覚もなく、そこそこの実力を付けていたように思います。自分にとって稽古することが自然なことだったのでしょう。特にしんどいと思ったこともなかったです。無駄な力を抜き全身をうまく使うことを合気道の稽古から学んでいたはずなのに、それが勉強の方には生かせていなかったのです。
+</p>
<p>
-「すめらうんこ」
+在学中は社会に出て仕事をすることなど考えていなかったため、いざ就職活動となると業界を選ぶことも大変でした。かろうじて興味のあったのがコンピュータなので、それ関係の会社を覗いたりしましたが、本当に自分にあっているのか分からず一から考え直すことに。結局実家の会社を嗣ぐために同業大手に応募、採用されました。
</p>
<p>
-そうそれは、すめらみことの落しもの(droppings)。嗚呼不敬。
+ところがこの会社があまりにも合わず半年で適応障害になって辞めることに。以来実家に戻って畑をするかたわら寺に通っています。この間生き方についていろいろ考えた結果、社会にもまれて気付いたら定年しているより、もっと人間らしい老い方をしたいと思うに至ったのです。常識に縛られても結局その常識に裏切られるだけです。金銭的な豊かさはあまりにも脆いものでしょう。
</p>
+<p>
+このサイトは日々考えていることを記しておきたいと思い立ち上げました。周りの人にはあまり共感されない僕の考えですが、なにかの参考になれば幸いです。
+</p>
+
+<h2>著作権について</h2>
+<p>
+このウェブサイト上のコンテンツは、特に断りのない限り<a href="http://creativecommons.org/publicdomain/zero/1.0?ref=chooser-v1" rel="license noopener noreferrer">CC0 1.0</a>で公開します。</p>
]]></description>
</item>
<item>
-<title></title>
-<link>https://www.mtkn.jp/gallery/20231202.html</link>
-<guid>https://www.mtkn.jp/gallery/20231202.html</guid>
-<pubDate>Sun, 3 Dec 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1></h1>
-<time>2023-12-02</time>
-<img src="img/20231202.jpg">
-]]></description>
-</item>
-<item>
-<title>不死身の特攻兵</title>
-<link>https://www.mtkn.jp/books/978-4-06-288451-8.html</link>
-<guid>https://www.mtkn.jp/books/978-4-06-288451-8.html</guid>
-<pubDate>Thu, 23 Nov 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>不死身の特攻兵</h1>
-<div class="author">鴻上尚史 著</div>
-<time>2022-11-23</time>
-<p>大東亜戦争において何度も特攻を命じられるも生還した佐々木友次という人の話。ベテランのパイロットだったために陸軍の特攻第一号に選ばれ、死んでこいと言われたが爆弾を落して帰ってきた人である。それも何度も。</p>
-<p>第一章は著者が佐々木氏を知ったいきさつ。第二章は佐々木氏の伝記。第三章は著者の佐々木氏へのインタビュー。第四章は著者による戦争や特攻に関する考察。</p>
-<p>第一章は導入である。</p>
-<p>第二章は物語として面白かった。戦争や特攻といった理不尽なものに対して自分の信念を貫いた佐々木氏の生き方や考え方が痛快である。</p>
-<p>第三章のインタビューでは佐々木氏が繰り返し口にした寿命という言葉が印象的だった。人間は寿命がくれば死に、それまでは生きる、というものである。たとえ戦死であろうともそれがその人の寿命であるとの認識であるようだ。この考えにはとても共感できる。</p>
-<p>第四章はかなり客観的に書かれているように思う。その上で著者自身の考えを述べているので読みやすかった。しかし読み進めるにつれてだんだんともやもやが溜っていった。当時の日本を支配していた空気があまりにも重苦しい。しかもその空気は、現在の日本にあるものとほとんど同じであると感じた。ここ最近僕が感じているこの国の暗い部分は大東亜戦争のころからなにも変らずに受け継がれているようである。せっかく無様に負けたのに本当の意味での反省を一切していないためか、なにも変っていない。余りにも勿体無く、戦没者にも失礼である。</p>
-<p>もっと歴史を勉強し、今の生き方に反映しなければと考えさせてくれる本だった。</p>
-]]></description>
-</item>
-<item>
-<title>チルノ</title>
-<link>https://www.mtkn.jp/gallery/20231018.html</link>
-<guid>https://www.mtkn.jp/gallery/20231018.html</guid>
-<pubDate>Tue, 24 Oct 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>チルノ</h1>
-<time>2023-10-18</time>
-<img src="img/20231018.jpg">
-]]></description>
-</item>
-<item>
-<title>結束バンド</title>
-<link>https://www.mtkn.jp/gallery/20231005.html</link>
-<guid>https://www.mtkn.jp/gallery/20231005.html</guid>
-<pubDate>Tue, 24 Oct 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>結束バンド</h1>
-<time>2023-10-05</time>
-<img src="img/20231005.png">
-]]></description>
-</item>
-<item>
-<title>後藤ひとり</title>
-<link>https://www.mtkn.jp/gallery/20231007.html</link>
-<guid>https://www.mtkn.jp/gallery/20231007.html</guid>
-<pubDate>Fri, 20 Oct 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>後藤ひとり</h1>
-<time>2023-10-07</time>
-<img src="img/20231007.jpg">
-]]></description>
-</item>
-<item>
-<title>イソヒヨ</title>
-<link>https://www.mtkn.jp/journal/posts/20230810.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20230810.html</guid>
-<pubDate>Thu, 10 Aug 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>イソヒヨ</h1>
-<time>2023-08-10</time>
+<title>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</title>
+<link>https://www.mtkn.jp/computer/mailserver.html</link>
+<guid>https://www.mtkn.jp/computer/mailserver.html</guid>
+<pubDate>Fri, 19 Jan 2024 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>メールサーバー構築 on OpenBSD with OpenSMTPD and Dovecot</h1>
+<time>2024-01-19</time>
+<h2>はじめに</h2>
<p>
-最近イソヒヨが庭でよく遊んでいる。昨日も来た。部屋の窓からのぞきこんでも逃げない。里芋が植わっているあたりでなにかしている。突然里芋の方にとびかかったと思うと、そのまま隣の家の物干しまで飛んでいった。くちばしになにか太いものをくわえている。少しくわえ直してそのままどこかに行ってしまった。</p>
+少し前にさくらのVPSで独自ドメインのメールサーバーを構築した。当時はドキュメントを追うのに必死で構築手順を整理できていなかった。しかしスマホを使わない僕の数少い連絡手段なので、受信できなくなると困る。そこであらためてサーバーを構築し、その手順を確認することにした。あいかわらずDNSの知識は自信がない。セキュリティは自己責任で。</p>
-<p>
-里芋の葉には4、5日前からセスジスズメがいた。昨日の朝見たときはだいぶ太って目玉模様もはっきりしていた。元気に里芋を食いちらかしていた。イソヒヨが飛んでいった後確認するも姿が見えない。くわえていたのはセスジスズメだったのか。</p>
+<h2>使うもの</h2>
+<ul>
+<li>VPS: さくらのVPS (IPアドレスを<i>sss.sss.sss.sss</i>とする)</li>
+<li>OS: OpenBSD 7.4</li>
+<li>ドメイン: ムームードメイン (mtkn.jpとする)</li>
+<li>SMTPサーバー(MTA): OpenSMTPD 7.4.0</li>
+<li>IMAPサーバー(MDA): Dovecot 2.3.20</li>
+</ul>
+<h2>メールの仕組み</h2>
<p>
-少し前にも里芋にセスジスズメがついていたが、気づいたら一匹もいなくなった。その前にはニンジンについたキアゲハが一晩でいなくなった。犯人はこいつか。</p>
-]]></description>
-</item>
-<item>
-<title>サマータイムレンダ読んだ</title>
-<link>https://www.mtkn.jp/journal/posts/20230803.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20230803.html</guid>
-<pubDate>Thu, 3 Aug 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>サマータイムレンダ読んだ</h1>
-<time>2023-08-03</time>
-
+メールの送受信にはSMTPというプロトコルが使われ、この通信を担うのがSMTPサーバーと呼ばれるソフトウェアである。このサーバーはメールの転送を担うので、MTA(Mail Transfer Agent)とも呼ばれる。今回はMTAとしてOpenSMTPDというのを使う。手元のパソコン等で書いたメールは、SMTPの通信で送信側のMTAに送られ、そこから受信側のMTAに転送される。受信側のMTAでは届いたメールをユーザーごとにふりわけ、所定の場所に保存する。</p>
<p>
-サマータイムレンダを読んだ。弟が漫画を買ってリビングに置いて母や妹に読ませていた。ある日なにげなくパラパラめくり始めたのだが、気づけば全部読んでしまっていた。13巻で完結するコンパクトな漫画である。</p>
-
+受信側のユーザーは、手元のパソコン等から受信側のサーバーに繋ぎ、自分宛てのメールを確認する。この時に使われるのがIMAPというプロトコルである(POPというのもあるが今回は割愛)。IMAPによるこの通信を担うのがIMAPサーバーで、メールを配達する役割を担うことから、MDA(Mail Delivery Agent)とも呼ばれる。今回MDAとしてDovecotを使用する。</p>
<p>
-話の内容はサスペンスとしては面白かった。サスペンス自体を論評する知識はないが、少なくとも僕は楽しめた。話の構成とその展開が上手いのだと思う。</p>
+以上をまとめると以下のようになる:
+</p>
+<pre>
++------------+ +----------------+
+|MTA(mtkn.jp)|--SMTP->| MTA |
++------------+ +--example.com---+
+ ^ | MDA |
+ | +----------------+
+ | |
+ SMTP IMAP
+ | |
+ | v
++------------+ +----------------+
+|user@mtkn.jp| |user@example.com|
++------------+ +----------------+
-<p>
-ところが最後の方に来て少し残念な感じになった。展開はそれまで通りのものに加え、クライマックスの勢いが付いているのだが、黒幕の動機がどうもうすっぺらい。そんな幼稚なやつにふりまわされていたのかと思うと、話の展開が面白いだけになんとも空虚になる。300年前に生まれた神職がそんなこと考えるのかも疑問である。そして動機がただ利己的であるがゆえに、悪役がどこから見てもただの悪役でしかない。だからこそ結末はこの悪役を倒してめでたしめでたしとしかならない。その上途中でおこった取り返しの付かないはずのことも、結局うまいことひっくり返して何も失わずにハッピーエンドを迎えた。これもあまり好みではなかった。</p>
-<p>
-この辺に関して個人的には最近読んだ「風の谷のナウシカ」のコミックがよかった。こちらには完全な悪がでてこない。敵であってもそれぞれなにかしらの事情があり、それぞれの信念に基いて行動している。だからこそ悪を殲滅して終りにはならないし、敵に勝ってもやるせない。このすっきりしなさが物語に重層性を持たせているように思う。さらに起ったことは事実として定着し、覆ることがないというのも重要なテーマとしていたように思う。どんな過去であっても今の自分たちの生命を以って未来を切り開くというものである。題材も内容も全く違う作品なので比べてもしょうがないが。</p>
+</pre>
+<ol>
+<li>メールを書く。</li>
+<li>送信側のMTAに送る。</li>
+<li>受信側のMTAに転送する。</li>
+<li>受信者が受信側のMDAに新着メールの有無を確認する。</li>
+</ol>
<p>
-僕はそもそも死んで時間が戻るのは好きではない。人生の一回性が無くなってしまうからだ。しかしこの作品は死んで時間が戻るといっても、一応完全には取り返しの付かないようになっている。それもあって楽しめたのだが、最後の最後で全部なかったことにしてしまったのは残念である。</p>
+SMTPサーバーどうしが通信する際、スパムメールをフィルタリングする為、いくつかの方法で送信元の本人確認を行う。その際第三者としてDNS及び、サーバーのIPアドレスを管理している人(今回の場合はさくらインターネット)が登場する。DNSにはドメインをIPアドレスにひもづける情報が登録できる。またIPアドレスの管理人には、自分が借りているIPアドレスを自分のドメインにひもづける情報を登録してもらう。これらによって、メールの送信者がドメイン、IPアドレス双方から確認できるので、なりすましを防止できる仕組みである。</p>
<p>
-それから、時間が戻る際に主人公は意識が連続しているので、完全に同一人物といえるかもしれないが、その他の登場人物はどうなのか。戻る前と戻った後では同じ意識の人間なのか。主人公から見れば自分以外に主観は無いので、時間が戻っても以前と同じ人間に見えるだろうが、しかしそれでいいなら本人もコピーされた影も同じだということにならないだろうか。この点について、どこかで問題提起されていたと思うが、結局回収されずにおわった。最後のハッピーエンドはあくまでも本人のみのものであり、それまでに死んだ他の登場人物は結局そのままなのではないだろうか。これもひとつの大きなテーマだと思っていただけに、どうも疑問が残る。</p>
+最近はgmail等の大手がスパム対策を強化する一環として、以下に述べるような本人確認をすべて通らなければ受信しないようにしているらしい。</p>
+<h2>MTAどうしのやりとり</h2>
<p>
-最後に、「あっぽけ」って言ってる人は見たことがない。「今日わは...」については大阪人の友達に言ってきょとんとされた経験がある。</p>
-]]></description>
-</item>
-<item>
-<title>着物できた</title>
-<link>https://www.mtkn.jp/journal/posts/20230802.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20230802.html</guid>
-<pubDate>Wed, 2 Aug 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>着物できた</h1>
-<time>2023-08-02</time>
+以下、MTAどうしの通信内容をざっくり説明する。
+</p>
+<h3>Aレコード、MXレコードによる送信先の特定</h3>
<p>
-作っていた着物が完成した。思っていたよりもずっと簡単だった。細かいところを追求すれば職人技なのだろうが、日常的に使うものを作るには素人でも十分である。</p>
+MTAがメールを送信する際、まずは宛先のサーバーを特定する必要がある。この際に使用するのがDNSに登録されているMXレコードである。送信元のMTAはまずDNSに宛先のドメインのMXレコードを問い合わせる。するとメールの送信先のドメイン名が分かるので、続いてこの送信先のIPアドレスを問い合わせる:</p>
+<pre>
+MTA (mtkn.jp <i>sss.sss.sss.sss</i>) DNS
+
+ example.com宛のメールって
+ 誰に送ったらええん?
+ ---------------------------------------->
+
+ mai.example.comやで(MXレコード)
+ <----------------------------------------
+
+ mail.example.comってどこや?
+ ---------------------------------------->
+ <i>ddd.ddd.ddd.ddd</i>やで(Aレコード)
+ <----------------------------------------
+</pre>
<p>
-初めは白衣のつもりで安物の晒をそのまま使って作り始めたが、白衣を着ることが無くなってしまったので、袖と衿だけ柿渋で染めて襦袢にすることにした。形は長着だが。</p>
+次にこの宛先のMTAとの通信を開始する。しかし受信側のMTA(example.com)は、mtkn.jpを名乗るサーバーが本当にmtkn.jpかどうか確認する必要がある:</p>
+<pre>
+MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
+ こんちは。mtkn.jpです。
+ ---------------------------------------->
+
+ なりすましちゃうか確認したろ
+</pre>
+<h3>SPFレコードによる送信元の本人確認</h3>
<p>
-完成後、右の衿がかなりずれて付いていることに気付いた。このときは疲れからか不幸にもこのままでいいやと思っていたが、見れば見るほど気になって、とうとうこの部分だけ付けなおすことにした。そして完成したのがこれである。</p>
-<img src="20230802.jpg" alt="初めて自作した着物の写真" />
+受信側のMTAは、自分に連絡してきたサーバーが本人かどうかをDNSに問い合わせる。このときに使うのがSPFレコードである。このレコードにはドメインの所有者が、自分のドメインからメールを送信してもいいIPアドレスを指定する。受信側のMTAは送信元のIPアドレスと、このレコードに記載されているIPアドレスを比べることで、送信側のMTAがドメインの所有者に認められているMTAかどうか判断できる。</p>
+<pre>
+DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
+ mtkn.jpを名乗るやつから連絡
+ 来てるんやけどこいつ本物?
+ <----------------------------------------
+
+ IPアドレスが<i>sss.sss.sss.sss</i>
+ やったら多分本物やわ。(SPFレコード)
+ ---------------------------------------->
+
+ よっしゃ
+</pre>
+<h3>PTRレコード</h3>
<p>
-ほんのひと昔前までは着物を作る人などその辺にいくらでもいた。世話になっていた寺の住職のお婆さんは和裁の先生だったそうだし、僕の祖父母より少し若いお茶屋さんは、自分のお母さんが家族の着るものを自分で作っていたという。つまり僕の3、4世代前は着物を自作するのが当たり前だった。ところがこの50年程の間にだれも作らなくなってしまった。和服に限らず自分で作れば安上りなのに。</p>
+次は逆にIPアドレスからドメインを辿って本人確認する。これに使われるのがPTRレコードである。このレコードはIPアドレスの管理人(ここではさくらインターネット)でないと登録できない。しかし、IPアドレスを借りるにあたって、自分の名前やら住所やらを提供しており、PTRレコードをきちんと設定した上でスパムメールを送ると、自分の身元がバレるので、スパムの抑止に繋がる。</p>
+<pre>
+DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
+ <i>sss.sss.sss.sss</i>ってIPアドレスで
+ mtkn.jpってドメイン登録されてる?
+ <----------------------------------------
+
+ されてるで。(PTRレコード)
+ ---------------------------------------->
+
+ よっしゃ
+</pre>
+<h3>DKIM</h3>
<p>
-なぜ自作しないのかと聞くと、きっと「そんな時間はない」という答えが返ってくる。金銭的にゆたかになって、自分で服を作る必要がなくなった。のではなく、服を作るような時間的余裕のある人がいなくなったというのが実情である。はて。</p>
-]]></description>
-</item>
-<item>
-<title>無題</title>
-<link>https://www.mtkn.jp/journal/posts/20230729.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20230729.html</guid>
-<pubDate>Sat, 29 Jul 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>無題</h1>
-<time>2023-07-29</time>
+送信側MTAが本人であることを証明するため、受信側に送るメールに署名する。署名は公開鍵方式で行うが、この鍵をドメインキーというようである。この署名が付いたメールをDKIM(DomainKey Identified Mail)という。送信側はあらかじめDNSにドメインキーの公開鍵を登録しておき、メール送信時に秘密鍵で署名する:</p>
+<pre>
+MTA (mtkn.jp <i>sss.sss.sss.sss</i>) MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
+
+ 本人確認できたわ
+ おまたせ。
+ <----------------------------------------
+
+ userさんにメール送るで。
+ 署名も付けるで。
+ ---------------------------------------->
+</pre>
<p>
-最近持っている浴衣がどれもぼろぼろになってきた。元々古いもらいものなうえ、着方も動き方もなってないので着ているとすぐにどこか破れる。補修しながら使っていたが、いよいよ布自体がすり切れてきた。いい機会なので自分で仕立ててみようと思いたった。
-実は寺に居たころに白衣を作ろうと思い、晒の裁断と袖の縫いまでしていた。ほったらかしになっていたこれを再開することにした。</p>
+受信側はDNSから送信側のドメイン鍵の公開鍵をダウンロードし、署名を確認する:</p>
+<pre>
+DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
+ mtkn.jpが署名付きでメール
+ くれたんやけどこの署名本物?
+ <----------------------------------------
+
+ これで確認してみて(DKIM公開鍵)
+ ---------------------------------------->
+
+ よっしゃ
+</pre>
+
+<h3>DMARCレコード(上記の本人確認で詐欺だと発覚した場合)</h3>
<p>
-腋縫いまでしていたこの着物に、今日はおくみを片方だけ付けた。両方したかったが疲れたのでもう片方はまた今度。色が付いていないただの晒だが、白衣など着る機会が無くなってしまったので、袖と襟だけでも染めることにした。家にころがっていたターナーの無臭柿渋を3倍に薄めて染めた。本当は自分で育てた柿を使いたいが、そんん場所はないので買ってきた渋である。柿渋は紫外線で硬化して定着するらしく、媒染剤は必要ないようである。襦袢くらいには使えるだろうか。</p>
+この他DNSにはDMARCレコードというものを登録することができる。これは、以上の本人確認により送信側がにせものであることが確認できた際、受信側にどのような行動を取ってほしいかを記述するものである。これにより例えば、自分を名乗る詐欺師がどこかにメールを送信した場合、受信したサーバーから自分宛てに通報するように頼める(この頼みが聞き入れられるかどうかは多分受信側MTAによる)。
+</p>
+<pre>
+DNS MTA (example.com <i>ddd.ddd.ddd.ddd</i>)
+ mtkn.jpを名乗る詐欺師から
+ メールきたんやけどどないしょ?
+ <----------------------------------------
+
+ ここ(postmaster@mtkn.jp)に通報や。
+ (DMARCレコード)
+ ---------------------------------------->
+
+ よっしゃ
+</pre>
+<h2>サーバーの設定</h2>
+<h3>VPSの契約、ドメインの取得</h3>
<p>
-梅を塩に漬けているのを思いだしたのでこれを庭に干した。県民の義務である。そして今日は土曜である。本当は自分で育てた梅を使いたいが、そんな場所はないので買ってきた梅である。残った梅酢は生姜でも漬けようか。</p>
+VPSとドメインを契約する。今回はVPSをさくらインターネットで、ドメインをムームードメインでそれぞれ契約した。さくらのVPSは好きなISOからOSをインストールでき、管理画面も分かりやすい(他のVPSを知らないので比較はできないが)。ムームードメインは安い。管理画面も悪くはない。以前お名前.comでも借りたことがあるが、こちらは広告のメールがやたら届いてうっとうしかった記憶がある。</p>
+<h3>OpenBSDのインストール</h3>
<p>
-庭に植えた里芋から芽がでてきた。大野芋というものであるが、僕が住んでいる場所とは関係がない。既に大きな葉が付いていたのだが、セスジスズメという芋虫にやられて禿げていた。四匹いたのだがある晩二匹減り、二日後にもう一匹、そしていつのまにか最後の一匹が居なくなっていた。なにかに食べられたのか。蛹は見ていない。以前ニンジンの花がキアゲハにやられた時も、たくさん居た芋虫が一晩で一匹も居なくなっていた。ところで芋虫というのは見ているとなんだか美味しそうに見えてくる。実際美味しいらしい<sup>[1]</sup>が。まだ食べる勇気はない。</p>
+さくらのVPSにOpenBSDをインストールする。さくらインターネットが用意したISOがあるので、OS再インストールの画面からそれを選べばすぐにインストールきる。でもなんとなく気持悪いのでopenbsd.orgから本家をダウンロードした。</p>
<p>
-この里芋は葉だけでなく茎を根本までかじられて切り株のようになっていたが、その切り株の中心が少しづつ盛り上がってきている。芋は強い。
+インストールのこまかい手順は割愛するが、ひとつだけひっかかった点があるので書いておく。インストール先のディスクをセットアップし、OSに必要なファイルをインストールメディアからディスクにコピーすると以下のエラーがでてカーネルパニックになった:
+</p>
+<pre><code>wdc_atapi_start: not ready, st = 50
+fatal protection fault in supervisor mode
+trap type 4 code 0 rip ffffffff810081a9 cs 8 rflags 10282 cr 2 23a896000 cpl 6 rsp ffff80000e9df410
+gsbase 0xffffffff81908ff0 kgsbase 0x0
+panic: trap type 4, code=0, pc=ffffffff810081a9
+syncing disks...18 18 18 18 18 18 18 18
+</code></pre>
+<p>
+原因はあまり調べていないが、どうもインストールメディアの読み込みに失敗しているようで、インストールに必要なファイルをウェブ上からダウンロードする方法を選択すると問題なくインストールできた。
</p>
+<h3>ファイアーフォールの設定</h3>
+<p>
+メールの送受信等に必要なポートを開ける:</p>
<ul>
-<li>[1]<a href="https://survivalnature.sakura.ne.jp/0000/11_mushi/imomushi/0000.html">イモムシ・ケムシ.サバイバル節約術</a></li>
+<li>TCP 22: ssh接続用</li>
+<li>UDP 53: DNSとの通信用</li>
+<li>TCP 25: SMTPの通信用</li>
+<li>TCP 587: メールクライアントからMTAへの通信用(メール送信時)</li>
+<li>TCP 993: メールクライアントからMDAへの通信用(メール受信時)</li>
+<li>TCP 80, 443: <code>acme-client(1)</code>によるサーバー証明書の取得用</li>
</ul>
-
-]]></description>
-</item>
-<item>
-<title>GOPL読んだ</title>
-<link>https://www.mtkn.jp/journal/posts/20230726.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20230726.html</guid>
-<pubDate>Wed, 26 Jul 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>GOPL読んだ</h1>
-<time>2023-07-26</time>
-<p>
-Alan A. A. DonovanとBrian W. Kernighanの「The Go Programming Language」を読了した。読み初めた日付は記録していないが、演習問題用のgitリポジトリは、今年の5月19日が最初のコミットである。二ヶ月ちょっとかかった。本文だけでなく演習問題も濃かった。時間をかけて読む価値があると思う。Kernighanの書いた本は英語が綺麗でいい。</p>
-<p>
-アマゾンで英語の原著を買ったが、表紙の紙のコーティングみたいなビニールが剥れてきているのが残念。</p>
-]]></description>
-</item>
-<item>
-<title>水道と空調</title>
-<link>https://www.mtkn.jp/journal/posts/20230725.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20230725.html</guid>
-<pubDate>Tue, 25 Jul 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>水道と空調</h1>
-<time>2023-07-25</time>
-
-<h2>水道</h2>
-<p>
-水道が普及して井戸が使えなくなった。水道があれば井戸から飲み水を汲む必要がない。井戸から飲み水を汲まなくなれば井戸水が無くなっても、あるいは汚染されても問題ない。問題なければその通りになる。水源を涵養するという考えが忘れられ、森がコンクリートで覆われた。水が汚なくても良くなったので農薬や肥料を大量に使うようになった。そして井戸は水位が下り、汚染された。</p>
-<p>
-便利な水道が普及した為に、水道が無いと不便な世界になった。不便というよりそれがないと生きるのもままならない。現在我々はそのインフラの維持費と、災害時の脆弱性に悩まされている。</p>
-<h2>空調</h2>
-<p>
-空調が普及したら気温が高くなった。逆ではない。空調があれば気温が高くなっても問題ない。問題なければその通りになる。街から植物が一掃されコンクリートとアスファルトで覆われた。暑くても問題ないので経済優先で自動車の為の道路ばかり整備された。そして街の気温は上った。</p>
-<p>
-便利な空調が普及した為に、空調が無いと不便な世界になった。不便というよりそれがないと生きるのもままならないようになりつつある。</p>
-<p>
-そのうち空調の効率を上げるために、都市全体が大きなドームに覆われる。ドームの外は50℃か60℃か。このインフラもまた維持費と災害に対する脆弱性で我々を悩ませることになろうか。</p>
-]]></description>
-</item>
-<item>
-<title>新型コロナウイルスとその対策に関する個人的見解</title>
-<link>https://www.mtkn.jp/journal/posts/20220730.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20220730.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>新型コロナウイルスとその対策に関する個人的見解</h1>
-<time>2022-07-30</time>作成<br>
-<time>2022-08-03</time>更新<br>
-<h2>はじめに</h2>
-<p>
-新型コロナウイルスとその対策に関していろいろと思うことはあるが、自分のなかでもあまりまとまっていないうえ、前提となる知識や情報、考えの論理的整合性がとれていない部分が多々ある。それもあってあまり文章として僕の考えを書こうとは思っていなかった。しかし今回の感染症が流行りだして2年と半年が経ち、いまだに社会全体がおかしい(と僕が思う)状態である。そのため自分も黙っているのはむしろ無責任ではないかと思いここに書き残すことにした。どうせほぼ見てくれる人はいないだろうけれど、それでもなにかしたかった。
-</p>
-<p>
-僕自身はどんなことがあろうがそれを運命と呼び、それを受け入れる覚悟である。その方が日々楽しく生きて納得の上で死んで行けるように思うのだ。自分でも素人なりに論文を読んだりして可能な限り信頼性の高い情報を集めようとはしているのだが、このような考えが根底にあるので、科学的な根拠というのはなんだかどうでもよく思えてきたりするもので、どうも今持っている知識というのがふわっとして正確性に欠ける。しかしだからこそ書きとめて後で反省できるようにしたい。
-</p>
<p>
-そのためこの文章は正確な知識に基くものではない。本来論文等をきちんと引用するべきところだが、とりあえず現時点での考えとして公開することにした。また、自分では気を付けているつもりだが、知らないうちに陰謀論を掴まされている可能性もある。おかしな点は指摘してくれるとありがたい。
+以上の設定を<code>/etc/pf.conf</code>に設定する:
</p>
+<pre><code>set skip on lo
-<h2>概要</h2>
-<p>
-マスクは今回の感染症を防ぐ効果は非常に限定的だと思う。一方でその副作用は大きい。体が常に酸素の薄い状態になっている。夏は熱中症の危険もある。却って口が乾燥するという人もいる。なによりの懸念は子供の発育への影響である。他人の表情が見えなくてどうしてまともに育つのか。
-</p>
-<p>
-ワクチンについても効果は薄く、副反応が大きいので打つべきではないと思う。特に若年層は発症した際のリスクが低く、逆にワクチンによるものと思われる心筋炎等の可能性が高い。さらに打ったからといって感染する確率は変らないようだ。
-</p>
-<p>
-消毒もやらない方がいいと思う。
-</p>
-<p>
-またこの病気自体危険性がそこまで高くないので、そもそも感染の拡大を防ぐ必要はないと思う。
-</p>
-<p>
-さらに、例え危険な病気が流行したとしても、人間味のある生活を犠牲にすることのデメリットとその病気の危険性とを天秤にかけ、どのような対策が適正なのかをきちんと議論すべきである。
-</p>
-<p>
-ところがそのような冷静な考察をする上で必要な情報がなにひとつみあたらない。感染者数や死者数として発表される数字は統計として信用できないだけでなく、医療機関に対する補助金等により大きく歪められている可能性が高い。
-</p>
-<p>
-また、マスクやワクチン等は同調圧力により行っている人が多いと思う。周囲の人間がやっているからといった理由だけでの行動である。このような世間の流れは非常に危険だと思う。今回は軽い風邪でしかなかったが、この程度のことでここまで社会全体がひとつの空気に支配されているようでは、本当に国運を分けるようなことに見舞われればひとたまりもない。意見の多様性を無視してひとつの流れを作ってしまうと、その流れが間違っていたときに国が滅びるだろう。
-</p>
-<p>
-しかしやはり一番心配なのは今このような環境のなかで自我を形成している子供である。マスクやワクチンはそれ自体が非常に強く影響する上、同調圧力も重くのしかかる。こんな状態で子供が健全に育つとはとても考えられない。10年、20年あるいはもっと先かも知れないが、今の子供達がこの国を動かすようになれば必ず大きな問題があちこちで噴出することになるだろう。
-</p>
-<p>
-そもそもウイルスは敵ではない。生態系の重要な一員である。共存していけばいいのだ。
-</p>
+block return # block stateless traffic
+pass in log proto tcp from any to any port 22 #ssh
-<h2>マスクについて</h2>
+pass out proto udp from any to <i>DNSのIPアドレス</i> port 53 #DNS
+pass out proto udp from any to any port 123 #ntp
+
+pass proto icmp #ping
+pass proto tcp from any to any port { 80, 443 } #www
+pass proto tcp from any to any port { 25, 587, 993 } #mail
+</code></pre>
<p>
-一応いろいろな論文を読んだ。マスクの効果に肯定的なものも否定的なものもあった。僕が読んだものはTwitterで拡散されているものが中心であるが、そのなかでマスクの効果に肯定的なものは、電話やメール等による調査とシミュレーションによるものばかりだった。前者は論文の中でも明記されているが、回答するかしないかは回答者に委ねられており、また回答者のバイアスもあり、とても信頼できるものとは言えないと思う。後者についてはマスクには定性的には効果があるとの前提に立って、その効果を定量的に測るものであるため、こちらも本当に意味があるものかどうか相当あやしいと思う。日本においてもスーパーコンピュータの富嶽によるシミュレーションを何度も目にしたが、これもマスクにより飛沫が防げると言っているだけで、だからウイルスに対してどう影響があるかという点については説明していない。マスクをすれば飛沫が飛びにくくなるのはスーパーコンピュータで計算するまでもなく理解できることではないか。一方でマスクの効果に否定的な論文には、RCT等、無作為性の高い手法を用いたものが多く、こちらのほうが信頼性が高いと判断した。ところでウイルスがマスクの繊維の隙間の大きさよりも圧倒的に小さいのでマスクに効果がないと言う人もいるがこれについてはなんとも言えないと思っている。マスクの穴が大きくても、例えば繊維に静電気で吸着されたりするかもしれない。このような可能性だけで議論すればいつまでも答えがでない。やはり信頼できる根拠というのは現実の観測であって無作為性の高い統計的手法によるものだと思う。
+<code>pf(4)</code>の設定を反映:
</p>
+<pre><code># pfctl -f /etc/pf.conf
+</code></pre>
+
+<h3>DNSの設定</h3>
+<h4>A, MX, SPF, DMARC</h4>
<p>
-続いてマスクの害について、先日マスクを着用しないと参加できないという場にどうしても行く必要があり、久々に着用した。あまりにも苦しかった。自分が喘息を持っているということもあるかもしれないが、明かに脳に届く酸素が少いと思う。これはバイアスかなあ。このような状態で2年以上も過している人の脳にはなにかしらの障害が起きていてもおかしくないと思うのだが。あるいはマスクにより口呼吸になり却って喉が乾くという話もある。喉が乾けばウイルス等に対する防御も手薄になるだろう。また不織布マスクからはマイクロプラスチックが放出され、それを大量に吸い込み続けているという話も聞く。マイクロプラスチックを吸い込むことと肺癌とを結び付ける話もあるがこの辺はあまりきちんと確かめていない。他にもいろいろなリスクがあるだろうが、一番重大なものはやはり子供の発育である。生れたときから周囲の人間がマスクをした状態で、どうして人間の表情や感情、あるいは言語を学ぶというのだろう。生まれたての赤ちゃんでなくても、他人との人間関係を学び自我を形成している最中の子供達にとって、周囲の表情が分からないのはあまりにも影響が大きいのではないだろうか。大人でもマスクをした他人の表情を読みとるのはマスクをしていない場合よりも難しいが、子供の場合はそれがさらに顕著であるという。このように社会全体が長期に亙ってマスクで顔を覆っていては、今の子供達の世代が将来酷く歪んだものになると思う。これに関して警鐘を鳴らしている人はいるが、社会全体では無視されている。これには科学的根拠はあまりないようだ。結果が出るのが数十年後であろうから当然である。しかしだからといって子供を虐げていい理由にはならないだろう。子供がまともに育たずに害を被るのは彼らが成長したときに年寄になっている今の我々自身かも知れない。ところで徳川時代において、徳川家の子供は生まれた時から尊いというので、乳母は母乳を与える際に顔を見せないようになにか被っていたらしい。それとの因果関係は不明だが、徳川家の子供は当時の平均よりも死亡率が高かったという。この話もきちんと確かめなければ。一方マスクのせいで子供の方が表情を作れなくなるのではないかと考えていたこともあるがこれについてはどうやら心配ないかもしれない。チンパンジーかなにかの実験で、子供の時から他の個体の表情を見せないように育てても、本人は表情を作ることに障害はなかったそうだ。表情の作り方は本能に実装されているらしい。
-</p>
+DNSのレコードを設定する。ムームーDNSの場合、コントロールパネルにログインし、サイドバーからドメイン管理>ドメイン操作>ムームーDNSと進み、設定するドメインの変更ボタンをクリックすると設定画面がでるので、以下のように設定する:</p>
+<table>
+<thead>
+<tr>
+<th>サブドメイン</th>
+<th>種別</th>
+<th>内容</th>
+<th>優先度</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td></td>
+<td>A</td>
+<td><i>sss.sss.sss.sss</i></td>
+<td></td>
+</tr>
+<tr>
+<td>mail</td>
+<td>A</td>
+<td><i>sss.sss.sss.sss</i></td>
+<td></td>
+</tr>
+<tr>
+<td></td>
+<td>MX</td>
+<td>mail.mtkn.jp</td>
+<td>10</td>
+</tr>
+<tr>
+<td></td>
+<td>TXT</td>
+<td>v=spf1 mx -all</td>
+<td></td>
+</tr>
+<tr>
+<td>_dmarc</td>
+<td>TXT</td>
+<td>v=DMARC1;p=reject;rua=mailto:postmaster@mtkn.jp</td>
+<td></td>
+</tr>
+</tbody>
+</table>
-<h2>ワクチンについて</h2>
<p>
-ワクチンの効果は非常に限定されているようである。当初は一回打てば感染しなくなり、今回の騒動も収まると言っていたように思う。当時からそんな訳ないやろとは思っていたが、案の定全く収束する気配はない。二回目には打てば重症化しなくなり、感染の拡大も終わると言い、三回目もこれで終りだと言っていた。そして今は四回目である。僕は一回も打っていないが、これまで政府の言うことを信じて打って来た人は未だに感染の拡大が収まらないことをどのように考えているのか不明である。更に四回目を打てと言われれば未だにその指示に従う理由もよく分からない。
+ひとつめのAレコードは、ドメイン名(mtkn.jp)にIPアドレス(<i>sss.sss.sss.sss</i>)を紐付けるものである。ふたつめはmail.mtkn.jpのIPアドレスを登録するものである。みっつめのMXレコードは、このドメイン宛てのメール(user@mtkn.jp等)をどこのサーバーに送信するかを決めるものである。上記の設定では、mail.mtkn.jpに送信するようにしている。以上の設定で、user@mtkn.jp宛てのメールは、mail.mtkn.jpというサーバーに送信すればよいことが分かり、さらにこのサーバーのIPアドレスは<i>sss.sss.sss.sss</i>であることも分かる。
</p>
<p>
-また副反応も大きいではないか。いちいち高熱がでて苦しんでいる人が沢山いる。僕の父も三回目にやたら高熱がでて仕事の最中に会社から帰って来て家で寝ていた。心筋炎の危険も高いようである。政府がデータをちょろまかしていたと言う話があったが、ワクチンを打った場合と打たなかった場合では打った場合の方が圧倒的にリスクが高いようだ。そもそも今回のワクチンは従来のものとは根本的に違う新しいものであり、その安全性も未知数である。特に長期的な影響についてはなにも分かっていないのではないか。もちろん新しい技術には常に危険が付いてまわるものである。それを理解したうえで打ちたいと言う人は本人の責任で打てばいい。ただし今回のように国民全員に打たせようというのは間違いである。こんなことをしてしまっては万が一この新しい技術に長期的な危険性があった場合に全国民がその影響を受けることになる。卵はひとつの籠に入れて運んではいけないのだ。
+次のTXTレコードはSPFレコードである。v=spf1でSPFのバージョンを示し、mxでドメインのMXレコードに登録されたIPアドレスからの送信を許可し、-allでそれ以外のIPアドレスからの送信を禁止している。
</p>
<p>
-ところでワクチンを打つのが無料だと思っている人が多いようだがそんな訳ないやろ。全部国の金であり、つまりは我々の税金である。国債でも発行してくれるのならまだいいが、東日本大震災の前例があるのでどうせ増税されるに決まっている。その時はぜひともワクチンを打った人にのみ課税してもらいたいものだ。僕は払いたくない。また万が一このワクチンに長期的な後遺症等の危険があることが判明でもすれば、その治療にも国民健康保険という名の税金が使われるのだろうと思うとやるせない。
-</p>
+最後のものはDMARCレコードである。v=DMARC1でバージョンを示し、p=rejectで、本人確認をクリアしなかったメールについて、その受信者が受信を拒否するように指定している。rua=mailto:postmaster@mtkn.jpでは、本人確認をクリアしなかったメールを受信した場合の通報先を指定している。</p>
-<h2>消毒について</h2>
-<p>
-消毒についても意味がないだけでなく逆効果である可能性もあると思う。手指消毒に関しては、一般に行われている程度のものではウイルスが死滅するとは思えない。ウイルスが死滅する程の消毒をいちいち行っていては、常在菌をも殺してしまい、さらに皮膚に備わっている物理的な免疫機構も働かなくなると思う。結局これも対策をしているというアピールの為のものでしかないのではないか。
+<h4>PTR</h4>
+<p>PTRレコードを設定する。さくらのVPSの場合、VPS管理画面にログインし、ネットワーク>ホスト名逆引き登録と進み、カスタムホスト名に自分のドメイン名(mtkn.jp)を入力する。
</p>
-<h2>新型コロナウイルス感染症の危険性について</h2>
+<h3>OpenSMTPDの設定</h3>
+<h4>ユーザーの作成</h4>
<p>
-この病気はそんなに危険なものであるとは思わない。死亡も重症化もそんなに数が多くない。また先日厚生労働省のウェブページのデータから計算してみると死者の平均年齢が男性で79歳程度、女性で83歳程度と、ほとんど平均寿命に近いものになっている。これは病気で死んでいるというよりも寿命ではなかろうか。ただしこの計算に使ったデータは年齢が10歳区切りであるのであくまでも概算である。病院がパンクしそうだという話も、どうやら軽傷の人が大量に流れこんできていて大変だということらしい。政府がこの感染症の扱いを5類に引き下げれば済む話のようだ。
-</p>
+メールの送受信を担当するユーザーを作成する:</p>
+<pre><code># useradd -m -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail
+</code></pre>
-<h2>人間味のある生活とのおりあいについて</h2>
+<h4>サーバー証明書の取得</h4>
<p>
-今回の騒動では強権的な対策が次々と打ち出された。マスクやワクチンの推奨は半ば強制力を伴ったものとなり、飲食店の営業自粛では法的拘束力が無いにしても、従わない店は名前を公開するなどされていた。個人的な行動も同様であった。それまであたりまえに行われてきた人間らしい生活が奪われた形になる。その後、因果関係は分からないが、自殺者が大きく増えたようだ。人間が行うことには必ず負の面がある。個人の自由を制限することにはそれなりのコストが伴うのだ。危険な感染症が流行したからといってただちに人権を制限することになりかねない今回のような対策をするのは間違っている。この対策により発生する種々のコストと、対策をしなかったときの危険を天秤にかけて考える必要があろう。命を守る為と言って一切の楽しみを奪われたのでは今すぐ死んだ方がましである。あるいは命を最優先にするにしても、その命の中には当然対策をしたがために自殺に追い込まれる人も入っているはずである。国という大きな組織を運営していく上ではなんらかの数字を見て次の行動を判断せざるを得ない訳だが、その数字として死者数を考えた場合でも、対策をしないがために死ぬ人の数と対策をしたために死ぬ人の数の双方を考慮せねばなるまい。
-</p>
+<code>httpd(8)</code>の設定ファイルを作成する。<code>/etc/examples</code>からサンプルの設定ファイルをコピーして編集する。変更箇所はドメイン名だけでいい。編集後、httpdを起動する。</p>
+<pre><code># cp /etc/examples/httpd.conf /etc/
+# vi /etc/httpd.conf
+# echo httpd_flags= >> /etc/rc.conf.local
+# rcctl start httpd
+# rcctl enable httpd
+</code></pre>
-<h2>必要な情報の欠如について</h2>
<p>
-上のような冷静な議論に必要なのが正確な情報である。感染症によって何人死んだのか、何人がどれほどの後遺症に苦しんでいるのか、逆に何人が感染症対策の為に死んだのか等の情報である。ところが今得られるそのような情報にはどれも正確さが欠如している。感染者数として発表されているのは無作為ではない方法でPCR検査をして陽性になった人の数である。東京都ではPCR検査を行う施設に補助金が出るので、接種を受ける人に商品券を配って集客している所もあると言う。そもそもPCR検査の開発者であるキャリー・マリス氏はこの検査は感染症の診断に利用すべきでないとの見解である。またこの感染症による死者数として発表されているのはあらゆる原因の死者のうちPCR検査により陽性だった人の数である。別の原因で死んでも陽性であれば新型コロナウイルス感染症による死者である。更に、新型コロナウイルス感染症により患者が死亡すればその病院に補助金が出ると言う話もある。結局この感染症の本当の姿というのは一切見えてこない。テレビ等ではひたすら上のような定義の感染者数ばかり報道して視聴者の恐怖を煽っているだけである。ただでさえいい加減な定義の数字をさらに補助金によって歪めているのが現状である。このような状況で市井の人々は一体なにを怖がっているのか不明である。
-</p>
+続いて<code>acme-client(1)</code>を使ってLet's Encryptでサーバー証明書を発行する。まずはこちらもサンプルの設定ファイルをコピーしてドメイン名を変更する。その後、<code>acme-client(1)</code>を実行し、さらに<code>cron(8)</code>に登録することで、証明書が自動で更新されるようにする。</p>
+<pre><code># cp /etc/examples/acme-client.conf /etc/
+# vi /etc/acme-client.conf
+# acme-client -v mail.mtkn.jp
+# crontab -e
+</code></pre>
-<h2>同調圧力について</h2>
-<p>
-このような状況において、結局人々はどうしていまだにマスクをしてワクチンを打つのかといえば、それは同調圧力によるものである。もちろん一部にはこのウイルスを本当に恐れている人もいるだろうが、僕の周りではかなり少数派である。むしろ周りの目を気にしている人が大多数だろう。あるいは単に世の中の流れだからという理由でそれに付き従っている人も少なくない。中には自分が苦しい思いをしてマスクを着けているのだからお前も苦しいのを我慢して着けろと言う人や、周囲が常識として受け入れていることなのだから自分もしないと恥をかくと言う人もいる。同調圧力を感じてそれに屈っしてマスクを着用している人も居るが、それでは結局自分自身がその同調圧力になり、それを維持してしまっているではないか。このようなことでは結局いつまでたってもやめられない状態に陥ってしまう。というか陥っている。
-</p>
-<p>
-僕は上に書いた通りマスクは効果も必要性もなく害があると思っているので着用しない。この考えが本当に正しいかどうかは正直よく分からないが、少なくともマスクを着ける効果や必要性がないと思っているのであれば周りの目など気にせずにその通り行動するべきだと思う。同調圧力に弱い人というのはおそらく社会の流れに合わせてさえいればその流れが間違っていても自分には責任が生じないと考えているのだろう。ところで僕は今のようにマスクを強要するような状態が長く続けば必ず取り返しの付かないような問題がでてくると思っている。特に子供は同調圧力による影響を受けやすいように思う。学校でマスクをしなかったりワクチンを打っていなかったりしていじめられるという話も聞く。彼らは大人になっても周囲を伺うばかりで何一つ判断できない人材になるだろう。このような人達が国を運営する時代がいずれ来る。そのような社会で老後を過ごさなければいけないのは結局この社会の流れに同調していた人自身である。社会の趨勢に任せておけば確かに明確に責任を追求されることはないかも知れないが、その結果の尻拭いをするのは自分自身なのだ。
-</p>
-<p>
-国を形作っているのは自分達自身である。国民とは別に政府が存在するのではなく、政府は国民自身が作るものである。個人とは別に社会の流れがあるのではなく、自分達が社会の流れそのものなのである。自分の頭で考え自分が信じたことを行動で周囲に示すのはむしろ社会に生きる一個人としての責務ではなかろうか。選挙で投票するのは国民の責務だというのと同じである。
-</p>
+<pre><code>#minute hour mday month wday [flags] command
+~ 2 * * * acme-client mail.mtkn.jp && rcctl restart smtpd
+</code></pre>
-<h2>意見の多様性について</h2>
-<p>
-同調圧力と関連するが、社会をいい方向に進める上で重要なのが意見の多様性である。ここに書いていることは僕個人の考えであり、それが正しいものかどうかはよく分からない。科学者の中にもマスクやワクチン等に関して種々の意見があり、統一されているものではない。そもそも科学は不完全なものなので科学者の意見が統一されてもそれが正しいかどうかはっきり言えるものではないだろう。個人の信条の問題も絡むといよいよ統一した見解というものを出すのは不可能である。しかしこのような意見の多様性というのがむしろ重要なのではないだろうか。多様な意見があればそのなかで最もいいものが生き残っていく。我々人間を含む生物というのはこのようにして進化し、生き残ってきたのではないだろうか。その結果が混沌のなかにある種の調和がとれたこの大自然というものであろう。
-</p>
+<h4>ドメインキーの作成とDNSへの登録</h4>
<p>
-ところが今のように一方の意見に統一して他方を無視し、あるいは排除しようとするのはあまりにも危険である。もし社会が選んだ方が間違っていたら人類は絶滅である。卵はひとつの籠に入れて運んではいけない。まあそのような世界になるのもひとつ楽しみであるかも知れないが。
+OpenSMTPD用のDKIMフィルターをインストールする。
</p>
+<pre><code># pkg_add opensmtpd-filter-dkimsign
+</code></pre>
-<h2>子供について</h2>
-<p>
-しかしなんといっても最優先して考えるべきは子供の未来ではないだろうか。今の感染症対策は結局のところ年寄の命を守る為に子供の生活を犠牲にしているというものである。別に対策などしなくてもこの病気で死ぬ年寄はこの病気でなくても死ぬだろうと僕は思うが、そうでなくてもこの感染症の死者および重症患者はやはり高齢者が中心であろう。そして高齢者を守る為であれば子供の生活や成長が犠牲になるということなど議論の俎上にも登らない。どのみちそう長くない高齢者の命と、この先国の中心を担っていく現在の子供達の生活や成長とどちらが大事なのかは考える余地もないと思うのだが現状そんなことを言っている人は少ないようである。今子供達の生活を鑑みずに自分達の命を最優先に考えて行動している年寄は恥を知れ。
-</p>
<p>
-子供への悪影響は多岐に渡る。マスクやワクチン、消毒等の直接の影響と、これらすべてに関連した同調圧力による影響である。これらは各項目の所で書いたのでここでは繰り返さない。
+ドメインキーとしてRSAの秘密鍵と公開鍵を作成する。DNSのTXTレコードの制約により、鍵の長さは1024ビットにする。
</p>
+<pre><code># openssl genrsa -out /etc/mail/dkim/private.key 1024
+# openssl rsa -in /etc/mail/dkim/private.key -pubout -out /etc/mail/dkim/public.key
+# chown -r _dkimsign:_dkimsign /etc/mail/dkim
+# chmod 0400 /etc/mail/dkim/private.key
+</code></pre>
-<h2>ウイルスは敵ではない</h2>
-<p>
-思うにウイルスは人類の敵ではない。彼らに感染することで他種の生物と遺伝情報のやり取りができると聞く。人間は人間だけで自然界を生きている訳ではない。互いに影響しあい、時には殺し合い、あるいは寄生しあい、生きているものである。そうしてあらゆる生物がその総体としてひとつの生態系を形成しているのだと思う。人間の命を最優先にして他の生命を排除するようではそのうち生態系の方から人類が見捨てられることにもなるだろう。ウイルスの方は我々と共存する道を探りながら進化している。人間の方もそれに応えるべきだ。
-</p>
+<p>作成したドメインキーの公開鍵をDNSに登録する:</p>
+<table>
+<thead>
+<tr>
+<th>サブドメイン</th>
+<th>種別</th>
+<th>内容</th>
+<th>優先度</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>selector1._domainkey.mail</td>
+<td>TXT</td>
+<td>v=DKIM1;k=rsa;p=<i>公開鍵</i></td>
+<td></td>
+</tr>
+</tr>
+</tbody>
+</table>
-<h2>最後に</h2>
<p>
-ここに書いたことは決して正確なことではない。僕の持っている科学的な知識や情報もいい加減なものである。ただし自信を持って言えるのは、僕は自分の頭で考えその結果正しいと思った行動を取っているということである。僕の考えが間違っていても、それは自分が正しいと思い、その通りに行動しているので、後で謝ることもできるし反省することもできる。自分で考えず、あるいは間違っていると思うことをしていては、後になって反省も謝罪もできない。そこにあるのは周りがやっていたから合わせただけで自分には責任がないという言い訳だけだ。しかしその結果どのような社会になろうが、自分もそこに住むことになるのだ。口で責任が無いとは言っても、結局その尻拭いは自分自身ですることになるのだ。今きちんと考えて自分の責任で行動しておかないと自分が死ぬ時にどんな顔をすればいいのか分からないだろう。食べてしまったケーキはとっておくことができないのだ。</p><div style="display: none;">別に爆弾は作らない。</div>
+サブドメインのselector1は以下のsmtpd.confで<code>filter-dkimsign</code>の-sオプションに
+指定してものと同じものであれば他のものでもいい。</p>
+
+<h4>smtpd.confの設定</h4>
<p>
-僕は他人に言われた通りやってつまらない人生を送るより、死ぬ危険があっても自分の信じたことをして生きたい。その方が死に直面したときに気分よく死ぬことができると思う。
-</p>
-]]></description>
-</item>
-<item>
-<title>x220から投稿テスト</title>
-<link>https://www.mtkn.jp/journal/posts/20210106.html</link>
-<guid>https://www.mtkn.jp/journal/posts/20210106.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>x220から投稿テスト</h1>
-<time>2021-01-06</time>
+/etc/mail/smtpd.confを以下の通り変更:</p>
+<pre><code># 認証に使用するサーバー証明書と秘密鍵を"mtkn.jp"という名前で登録。
+# 証明書のファイルは先程acme-client(1)で取得したもの。
+pki mtkn.jp cert "/etc/ssl/mail.mtkn.jp.fullchain.pem"
+pki mtkn.jp key "/etc/ssl/private/mail.mtkn.jp.key"
+
+# テーブルの登録
+# ローカルに届いたメールの転送を設定するテーブル
+table aliases file:/etc/mail/aliases
+# ユーザーの名前と暗号化されたパスワードのテーブル
+table passwd file:/etc/mail/passwd
+# メールの転送を設定するテーブル
+table virtuals file:/etc/mail/virtuals
+
+# フィルターの登録
+# ドメインキーによる署名を付加するフィルター
+filter dkim_sign proc-exec\ "filter-dkimsign -d mail.mtkn.jp -s selector1\ -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign
+# DNSの逆引き(PTRレコード)ができなかった場合に受信を拒否するフィルター
+filter check_rdns phase connect match !rdns disconnect "550 no rDNS."
+# 正引き(Aレコード)と逆引き(PTRレコード)でDNSのレコードが一致しなかった
+# 場合に受信を拒否するフィルター
+filter check_fcrdns phase connect match !fcrdns disconnect "550 no FCrDNS."
+
+# 各ネットワークインターフェース毎の処理を設定。
+listen on socket
+listen on lo0
+# vio0というインターフェースの25番ポートに届いたものに関する設定。
+# 外部からmtkn.jp宛に届いたものの処理である。
+# 最初に登録した証明書と秘密鍵を使ってMTAの本人確認をし、
+# 上で設定したDNSに関するフィルターを適応。
+listen on vio0 port 25 tls pki mtkn.jp hostname "mail.mtkn.jp"\ filter { check_rdns, check_fcrdns }
+# vio0というインターフェースのsubmission(587番)ポートに関する設定。
+# mtkn.jpのユーザー(e.g. user@mtkn.jp)から任意の人宛に送信される
+# メールに関する設定。
+# 上と同様に"mtkn.jp”という名前で登録されたpkiを使ってMTAの本人確認をし、
+# "passwd"という名前で登録したパスワードで認証し、
+# さらにドメインキーでメールに署名する。
+listen on vio0 mask-src port submission smtps pki mtkn.jp\ hostname "mail.mtkn.jp" auth <passwd> filter dkim_sign
+
+# 各種メールの処理を定義する。
+# 各処理が実行される条件はこの後定義する。
+# ローカルメールはデフォルトのまま
+action "local_mail" mbox alias <aliases>
+# 自分のドメイン宛のメールは/var/vmail/以下に振り分けてmaildir形式で保存。
+action "domain" maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}"\ virtual <virtuals>
+# 他のドメイン宛ては宛先へ転送。
+action "outbound" relay
+
+# どういう条件でどういう処理をするかを定義する。
+# 任意の送信元から自分のドメイン宛ては上で定義したdomainという処理をする。
+# ここでは/var/vmail/以下に振り分けて保存する。
+match from any for domain mtkn.jp action "domain"
+# このサーバー内でのメールはlocal_mailとして処理。
+match from local for local action "local_mail"
+# このサーバー内から他所へのメールは宛先に転送
+match from local for any action "outbound"
+# 外部から来た他所宛てのメールは認証されているものに限り転送。
+# authを忘れると多分このサーバー経由で迷惑メールが送られる危険がある。
+match auth from any for any action "outbound"
+</code></pre>
+
+<h4>各テーブルの作成</h4>
<p>
-thinkpad x220を手に入れ、arch linuxの環境を整えた。
-</p>
+上記の設定ファイルの<code>table</code>で指定したテーブルを作成する。<code>/etc/mail/aliases</code>は特に変更しなくてもいい。<code>/etc/mail/passwd</code>にはメールアカウントと暗号化されたパスワードを保存する。パスワードの暗号化には<code>smtpctl(8)</code>を使う:</p>
+<pre><code># echo user@mtkn.jp:$(smtpctl encrypt) >> /etc/mail/passwd
+<i>パスワードを入力し、エンター、Ctrl-D</i>
+</code></pre>
+
<p>
-スペックはこんな感じ:
-</p>
-<ul>
- <li>core i7 2620M</li>
- <li>メモリ 8GB</li>
- <li>ssd 256GB。</li>
-</ul>
+<code>/etc/mail/virtuals</code>にはメールの転送を設定できる。以下のようにする:</p>
+<pre><code>abuse@mtkn.jp user@mtkn.jp
+postmaster@mtkn.jp user@mtkn.jp
+webmaster@mtkn.jp user@mtkn.jp
+user@mtkn.jp vmail
+</code></pre>
<p>
-ヤフオクで送料入れて15000円程度だった。
+この設定で、例えばabuse@mtkn.jp宛てのメールがuser@mtkn.jpに転送される。abuse, postmaster, webmasterはなんか一応作っとけみたいに書いてたけど必要なんかな?
</p>
+
+<h3>Dovecotの設定</h3>
<p>
-画面はTNだろうと思って買ったが、
-届いたら視野角が広くて非常に見易いので
-多分IPSであろう。
-これは結構うれしい。
-</p>
+MDAとして<code>dovecot(1)</code>をインストールする。</p>
+<pre><code># pkg_add dovecot
+</code></pre>
<p>
-全体的に綺麗な個体だ。
+dhparamを作成する(時間がかかる):
</p>
+<pre><code># openssl dhparam -out /etc/ssl/dh.pem 4096
+</code></pre>
+
<p>
-バッテリーは期待通り完全に消耗していて、
-起動後5秒ほどで切れてしまう。
+わかりにくい設定ファイルをどけてシンプルなものに書き直す:
</p>
+<pre><code># mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
+# vi /etc/dovecot/dovecot.conf
+</code></pre>
<p>
-あとwindows用の日本語キーボードはあんまり
-好きではない。
-いちばん下の段がぎゅうぎゅう詰めでスペースが
-小さすぎる。
-</p>
+ドキュメントは公式ウェブサイトで確認できる。分量が多すぎて読む気にならない。サンプルの設定ファイルにも各項目の説明と既定値が書いてあるがこれも分かりにくい。パスワードが漏洩するのはとりあえず避けたいので、<code>ssl = required</code>と暗号化されていないIMAPを<code>port = 0</code>として無効にする。他はとりあえず以下の設定で動く:</p>
+<pre><code># IPv4は全て監視
+listen = *
+
+# SSL接続のみ許可
+ssl = required
+
+# SSLに使用するファイル
+ssl_cert = </etc/ssl/mail.mtkn.jp.fullchain.pem
+ssl_key = </etc/ssl/private/mail.mtkn.jp.key
+ssl_dh = </etc/ssl/dh.pem
+
+# メールの保存場所は各ユーザーのホームディレクトリ。
+# ホームディレクトリの場所は以下のuserdbで設定。
+mail_location = maildir:~
+
+# パスワードのファイル。
+# OpenSMTPDと共有。
+passdb {
+ args = scheme=BLF-CRYPT /etc/mail/passwd
+ driver = passwd-file
+}
+
+# ユーザーの設定。
+# ホームディレクトリは/var/vmail/<i>ドメイン</i>/<i>ユーザー名</i>
+userdb {
+ args = uid=vmail gid=vmail home=/var/vmail/%d/%n
+ driver = static
+}
+
+# 使用するプロトコル。
+protocols = imap lmtp
+
+# SSLを使ったIMAPSのみを許可。
+service imap-login {
+ inet_listener imaps {
+ port = 993
+ ssl = yes
+ }
+ # Disable imap
+ inet_listener imap {
+ port = 0
+ }
+}
+</code></pre>
+<pre><code># rcctl start dovecot
+# rcctl enable dovecot
+</code></pre>
+
<p>
-バッテリーを交換するのが先かキーボードをUS配列にするのが先か
-悩ましいところだ。
+サーバーの証明書が更新される際に<code>dovecot(1)</code>も再読み込みされるように<code>cron(8)</code>に追加しておく:
</p>
+<pre><code>crontab -e
+</code></pre>
+
+<pre><code>#minute hour mday month wday [flags] command
+~ 2 * * * acme-client mail.mtkn.jp && rcctl restart smtpd && rcctl reload dovecot
+</code></pre>
+
+<h3>テスト</h3>
+<p>
+手元のメールクライアントから送受信のテストをする。特にgmail等に送った際に迷惑メールに分類されないかどうか確認する。以上の設定で、gmailへの送受信は問題なかった。また、<code>neomutt(1)</code>、<code>thunderbird(1)</code>からログイン及び送受信できることも確認した。</p>
+
+<h2>参考文献</h2>
+<ul>
+<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-1-what-is-email.html">Arch Linux mail server tutorial - part 1 - What is email?.unixsheikh.com</a></li>
+<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-2-opensmtpd-dovecot-dkimproxy-and-lets-encrypt.html">Arch Linux mail server tutorial - part 2 - OpenSMTPD, Dovecot, DKIMproxy, and Let's Encrypt.unixsheikh.com</a></li>
+<li><a href="https://unixsheikh.com/tutorials/arch-linux-mail-server-tutorial-part-3-get-dns-right-it-is-important.html">Arch Linux mail server tutorial - part 3 - Get DNS right, it's important!.unixsheikh.com</a></li>
+<li><a href="https://man.openbsd.org/smtpd">smtpd(8).OpenBSD Manual Pages</a></li>
+<li><a href="https://man.openbsd.org/smtpd.conf">smtpd.conf(5).OpenBSD Manual Pages</a></li>
+<li><a href="https://man.openbsd.org/httpd">httpd(8).OpenBSD Manual Pages</a></li>
+<li><a href="https://man.openbsd.org/httpd.conf">httpd.conf(5).OpenBSD Manual Pages</a></li>
+<li><a href="https://man.openbsd.org/acme-client">acme-client(1).OpenBSD Manual Pages</a></li>
+<li><a href="https://man.openbsd.org/acme-client.conf">acme-client.conf(5).OpenBSD Manual Pages</a></li>
+<li><a href="https://doc.dovecot.org/">Dovecot manual — Dovecot documentation</a></li>
+</ul>
]]></description>
</item>
<item>
-<title>Xlibで遊んでみる6</title>
-<link>https://www.mtkn.jp/computer/xlib_playground6.html</link>
-<guid>https://www.mtkn.jp/computer/xlib_playground6.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる6</h1>
-<time>2023-01-25</time>
+<title>使用しているハードウェア、ソフトウェア</title>
+<link>https://www.mtkn.jp/computer/what-i-use.html</link>
+<guid>https://www.mtkn.jp/computer/what-i-use.html</guid>
+<pubDate>Sat, 13 Jan 2024 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>使用しているハードウェア、ソフトウェア</h1>
+<time>2024-01-11</time>更新<br>
+<time>2021-12-13</time>作成
+<h2>ノートパソコン</h2>
+<p>メインのパソコン。</p>
+<dl>
+<dt>ハードウェア: Macbook Air 2013</dt>
+<dd>ThinkPad辞めた。ファンがあまりにもうるさい。ThinkPadの前に使ってたMacbook Airを引っ張り出してバッテリー交換して使ってみたらファンがめっちゃ静か。多分ほぼ回ってない。M1 Macbook Airが欲しくなってきた。</dd>
+<dt>OS: Arch Linux</dt>
+<dd>本当はOpenBSDがいいんやけどMacbook AirだとWi-Fiも使えやんしいろいろサポートが悪いので...</dd>
-<p>
-前回: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
-</p>
-<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
-</p>
+<dt>Window Manager: <a href="https://git.mtkn.jp/dwm">dwm</a></dt>
-<h2>ワールドマップの作成</h2>
+<dt>Terminal: <a href="https://git.mtkn.jp/st">st</a></dt>
+
+<dt>Text Editor: acme, nvi</dt>
+<dd>vimは辞めた。OpenBSDにあったnviが丁度いい。日本語を使うのでUTF8に対応したnvi2を使っている。Arch Linuxだとちょっと使い勝手が違う。コマンド履歴が編集できないのが不便である。日本語入力が必要ない場面ではacmeを使っている。</dd>
+
+<dt>Browser: firefox</dt>
+<dd>もうちょっとましなのないんかな。UIころころ変わるし、重いし、嫌い。JavaScriptがないと困るので仕方なく使っているが...特に最近画面上部のUIが大きすぎて邪魔である。いまだに720pの液晶なのも悪いけど...このパソコンで上部にナビゲーションバーが固定されているページを見ると画面の半分近くがかくれてしまう...
+</dd>
+
+<dt>Mail Client: neomutt</dt>
+<dd></dd>
+</dl>
+
+<h2>デスクトップ</h2>
+<p>学生のときに組んだ。無駄にハイスペック。この上RTX2060も積んでたが、OpenBSDで使えないので解雇した。最近は絵を描くのに使っている。</p>
+<dl>
+<dt>マザーボード: ASRock Z390 Pro4</dt>
+<dt>CPU: Intel Core i7-9700K</dt>
+<dt>メモリ: センチュリーマイクロ 8GB * 2</dt>
+<dd>国産</dd>
+<dt>ストレージ: KimMiDi SSD 256GB</dt>
+<dd>ThinkPad買ったときについてきたやつ。</dd>
+<dt>モニタ: Eizo FlexScan EV2451</dt>
+<dd>綺麗やけど質量が大きい。</dd>
+<dt>OS: MX Linux</dt>
+<dd>Arch Linuxを使っていたが、GIMPでペンタブの筆圧感知を有効にすると落ちるので変更した。無難に使える。</dd>
+<dt>ソフト: GIMP<dt>
+<dd></dd>
+<dt>ペンタブ: wacom intuos comic CTH-480/S3</dt>
+<dd>小さいが十分使える</dd>
+</dl>
+
+<h2>家のサーバー</h2>
<p>
-ゲームのワールドマップを作製した。ここでは文字列として登録した。なにもないところは「<code>.</code>」、ブロックの場所は「<code>b</code>」、プレーヤーは「<code>p</code>」とした:
+バックアップ用。ノートパソコンのホームディレクトリのバックアップとGIMPで描いた絵を保存するのにも使っている。GIMPの絵は自作の9Pライブラリを使ってアクセスできるようにしている。
</p>
-<pre><code>char worldmap[WORLD_WIDTH * WORLD_HEIGHT + 1] =
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"........b......................................................................."
-"................................................................................"
-"................................................................................"
-"....b..........................................................................."
-"................................................................................"
-"................b..............................................................."
-"..........................................................b..........b.........."
-"................................................................................"
-".......................b........................................................"
-"...........................................b...................................."
-"...........................................b...................................."
-"................................................................................"
-"..................b............................................................."
-"................................................................................"
-"...........................................b...................................."
-"................................................................................"
-"................................................................................"
-"...........................b...................................................."
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"....................................bbbbbbbbbb.................................."
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"..............................................bbbbbbbbbb........................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"....................................bbbbbbbbbb.................................."
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"..........................bbbbbbbbbb............................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................................................................................"
-"................bbbbbbbbbb......................................................"
-"................................................................................"
-"................................................................................"
-"...p............................................................................"
-"bbbbbbbbbbbbbbbbbbbbbbbbb.......bbbbbbbbbbbbbbbbbbbbbbbb...bbbbbbbbbbbbbbbbbbbbb"
-"........................b.......b......................b...b...................."
-"........................b.......b......................b...b...................."
-"........................b.......b......................b...b...................."
-"........................b.......b......................b...b....................";
-</code></pre>
-
-<h2>プレイヤーの作成</h2>
-<p>プレイヤーには重力をかけたいので、まずは四角形に加速度を追加:</p>
-<pre><code>struct rect {
- float ppx, ppy;
- float px, py;
- float vx, vy;
- float ax, ay; // acceleration
- int w, h;
- int m;
-};
-</code></pre>
-<p>ワールドマップを読み込み、その際にプレイヤーに重力を付加:</p>
-<pre><code>struct rect block[NUM_RECT];
-struct rect player;
-
-/* ... */
+<dl>
+<dt>ハードウェア: Dell D520</dt>
+<dd>おじいちゃんの家にころがってたのをもらってきた。</dd>
+<dt>OS: OpenBSD</dt>
+<dd></dd>
+</dl>
- int bi = 0;
- for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
- if (world_map[i] == 'b') {
- block[bi].ppx = block[bi].px = i % WORLD_WIDTH * BLOCK_SIZE;
- block[bi].ppy = block[bi].py = i / WORLD_WIDTH * BLOCK_SIZE;
- block[bi].ax = 0;
- block[bi].ay = 0;
- block[bi].vx = 0;
- block[bi].vy = 0;
- block[bi].w = block[bi].h = BLOCK_SIZE;
- block[bi].m = block[bi].w * block[bi].h;
- bi++;
- } else if (world_map[i] == 'p') {
- player.ppx = player.px = i % WORLD_WIDTH * BLOCK_SIZE;
- player.ppy = player.py = i / WORLD_WIDTH * BLOCK_SIZE;
- player.vx = 0;
- player.vy = 0;
- player.ax = 0;
- player.ay = GRAVITY;
- player.w = player.h = BLOCK_SIZE;
- player.m = player.w * player.h;
- }
- }
-</code></pre>
+<h2>ウェブサーバー</h2>
+<p>このウェブページ用</p>
+<dl>
+<dt>VPS: さくらのVPS</dt>
+<dd>ちょっと高い(7078円/12ヶ月)。OpenBSDを入れられる。</dd>
+<dt>OS: OpenBSD</dt>
+<dt>httpサーバー: Openbsd httpd</dt>
+<dd>manが分かりやすい。</dd>
+<dt>smtpサーバー: OpenSMTPD</dt>
+<dd>manが分かりやすい。</dd>
+<dt>imapサーバー: dovecot</dt>
+<dd>manはあんまり分かり易くない。<dd>
+</dl>
-<p>ユーザーからの入力を受けとり、プレイーヤの加速度等を変更。<code>A</code>、<code>D</code>でそれぞれ左右に加速し、地面に接しているときに<code>space</code>キーでジャンプさせる:
+<h2>理想</h2>
+<dl>
+<dt>CPU: RISC-V</dt>
+<dd>はやく家庭用コンピュータの世界に来てほしい</dd>
+<dt>ファン: レス</dt>
+<dd>静音性は優先度がかなり高い。</dd>
+<dt>メモリ: 8GB</dt>
+<dd>そんないらん</dd>
+<dt>モニター: 60fpsでるカラーの電子ペーパー</dt>
+<dt>キーボード: PFU製</dt>
+<dd>macbook airに戻っても特にストレスないからキーストロークはそんなにいらんかな</dd>
+<dt>バッテリー: 24時間くらい</dt>
+<dt>OS: Plan9かOpenBSD</dt>
+<dd>ハードウェアは全部ドキュメント公開してくれ</dd>
+</dl>
+]]></description>
+</item>
+<item>
+<title>謹賀新年</title>
+<link>https://www.mtkn.jp/gallery/20240101.html</link>
+<guid>https://www.mtkn.jp/gallery/20240101.html</guid>
+<pubDate>Mon, 1 Jan 2024 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>謹賀新年</h1>
+<time>2024-01-01</time>
+<img src="img/20240101.jpg">
+]]></description>
+</item>
+<item>
+<title>Holy Shit!</title>
+<link>https://www.mtkn.jp/journal/posts/20221228.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20221228.html</guid>
+<pubDate>Thu, 28 Dec 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Holy Shit!</h1>
+<p>最近見ているプログラマの配信者がよく言っている。「Holy Shit!」Holyは聖なる、Shitはうんこという意味である。日本のプログラマもうんこという言葉を好むが、海外でも同じようである。
</p>
-<pre><code>void
-handle_inputs(int key_state[])
-{
- if (key_state[KEY_Q] == KEY_DOWN){
- next_menu = GAME_OVER;
- return;
- }
- if (key_state[KEY_D] == KEY_DOWN) {
- if (player.vx > 0) {
- player.ax = 500;
- } else {
- player.ax = 1000;
- }
- } else if (key_state[KEY_A] == KEY_DOWN) {
- if (player.vx > 0) {
- player.ax = -1000;
- } else {
- player.ax = -500;
- }
- } else {
- if (player_is_falling)
- player.ax = -player.vx;
- else
- player.ax = -3 * player.vx;
- }
-
- if (player.vx < -200) player.vx = -200;
- if (player.vx > 200) player.vx = 200;
- if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN)
- player.vy = -450;
-}
-</code></pre>
-
-<p>変更した加速度は<code>rect_next_tick()</code>関数で次の位置を計算するのに使用。また画面の下に落ちた時にゲームオーバーになるように設定:</p>
-<pre><code>void
-rect_next_tick(struct rect *s, long ndt) // nano second
-{
- s->ppx = s->px;
- s->ppy = s->py;
- s->vx += s->ax * ndt / 1000 / 1000 / 1000;
- s->vy += s->ay * ndt / 1000 / 1000 / 1000;
- s->px += s->vx * ndt / 1000 / 1000 / 1000;
- s->py += s->vy * ndt / 1000 / 1000 / 1000;
-
- // bind within the window
- if (s->px < 0) {
- s->px = 0;
- //s->vx *= -1;
- }
- if (win_width < s->px + s->w) {
- s->px = win_width - s->w;
- //s->vx *= -1;
- }
- // game over when fall out of the screen
- if (s->py > win_height)
- next_menu = GAME_OVER;
-}
-</code></pre>
-
-
-<h2>完成品</h2>
<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex6/ex6.c.html">git</a>
+ところでこの言葉の訳語はなにがいいかと考えていると思いがけずぴったりなものを思いついた:</p>
+<p>
+「すめらうんこ」
</p>
<p>
-<video controls>
-<source src="videos/ex6.webm" type="video/webm">
-</video>
+そうそれは、すめらみことの落しもの(droppings)。嗚呼不敬。
</p>
-
-<h2>参考</h2>
-<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-</ul>
]]></description>
</item>
<item>
-<title>Xlibで遊んでみる5</title>
-<link>https://www.mtkn.jp/computer/xlib_playground5.html</link>
-<guid>https://www.mtkn.jp/computer/xlib_playground5.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる5</h1>
-<time>2023-01-03</time>
+<title></title>
+<link>https://www.mtkn.jp/gallery/20231202.html</link>
+<guid>https://www.mtkn.jp/gallery/20231202.html</guid>
+<pubDate>Sun, 3 Dec 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1></h1>
+<time>2023-12-02</time>
+<img src="img/20231202.jpg">
+]]></description>
+</item>
+<item>
+<title>不死身の特攻兵</title>
+<link>https://www.mtkn.jp/books/978-4-06-288451-8.html</link>
+<guid>https://www.mtkn.jp/books/978-4-06-288451-8.html</guid>
+<pubDate>Thu, 23 Nov 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>不死身の特攻兵</h1>
+<div class="author">鴻上尚史 著</div>
+<time>2022-11-23</time>
+<p>大東亜戦争において何度も特攻を命じられるも生還した佐々木友次という人の話。ベテランのパイロットだったために陸軍の特攻第一号に選ばれ、死んでこいと言われたが爆弾を落して帰ってきた人である。それも何度も。</p>
+<p>第一章は著者が佐々木氏を知ったいきさつ。第二章は佐々木氏の伝記。第三章は著者の佐々木氏へのインタビュー。第四章は著者による戦争や特攻に関する考察。</p>
+<p>第一章は導入である。</p>
+<p>第二章は物語として面白かった。戦争や特攻といった理不尽なものに対して自分の信念を貫いた佐々木氏の生き方や考え方が痛快である。</p>
+<p>第三章のインタビューでは佐々木氏が繰り返し口にした寿命という言葉が印象的だった。人間は寿命がくれば死に、それまでは生きる、というものである。たとえ戦死であろうともそれがその人の寿命であるとの認識であるようだ。この考えにはとても共感できる。</p>
+<p>第四章はかなり客観的に書かれているように思う。その上で著者自身の考えを述べているので読みやすかった。しかし読み進めるにつれてだんだんともやもやが溜っていった。当時の日本を支配していた空気があまりにも重苦しい。しかもその空気は、現在の日本にあるものとほとんど同じであると感じた。ここ最近僕が感じているこの国の暗い部分は大東亜戦争のころからなにも変らずに受け継がれているようである。せっかく無様に負けたのに本当の意味での反省を一切していないためか、なにも変っていない。余りにも勿体無く、戦没者にも失礼である。</p>
+<p>もっと歴史を勉強し、今の生き方に反映しなければと考えさせてくれる本だった。</p>
+]]></description>
+</item>
+<item>
+<title>チルノ</title>
+<link>https://www.mtkn.jp/gallery/20231018.html</link>
+<guid>https://www.mtkn.jp/gallery/20231018.html</guid>
+<pubDate>Tue, 24 Oct 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>チルノ</h1>
+<time>2023-10-18</time>
+<img src="img/20231018.jpg">
+]]></description>
+</item>
+<item>
+<title>結束バンド</title>
+<link>https://www.mtkn.jp/gallery/20231005.html</link>
+<guid>https://www.mtkn.jp/gallery/20231005.html</guid>
+<pubDate>Tue, 24 Oct 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>結束バンド</h1>
+<time>2023-10-05</time>
+<img src="img/20231005.png">
+]]></description>
+</item>
+<item>
+<title>後藤ひとり</title>
+<link>https://www.mtkn.jp/gallery/20231007.html</link>
+<guid>https://www.mtkn.jp/gallery/20231007.html</guid>
+<pubDate>Fri, 20 Oct 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>後藤ひとり</h1>
+<time>2023-10-07</time>
+<img src="img/20231007.jpg">
+]]></description>
+</item>
+<item>
+<title>イソヒヨ</title>
+<link>https://www.mtkn.jp/journal/posts/20230810.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20230810.html</guid>
+<pubDate>Thu, 10 Aug 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>イソヒヨ</h1>
+<time>2023-08-10</time>
<p>
-前回: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
-</p>
+最近イソヒヨが庭でよく遊んでいる。昨日も来た。部屋の窓からのぞきこんでも逃げない。里芋が植わっているあたりでなにかしている。突然里芋の方にとびかかったと思うと、そのまま隣の家の物干しまで飛んでいった。くちばしになにか太いものをくわえている。少しくわえ直してそのままどこかに行ってしまった。</p>
+
<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
-</p>
+里芋の葉には4、5日前からセスジスズメがいた。昨日の朝見たときはだいぶ太って目玉模様もはっきりしていた。元気に里芋を食いちらかしていた。イソヒヨが飛んでいった後確認するも姿が見えない。くわえていたのはセスジスズメだったのか。</p>
-<h2>円の衝突判定とその処理</h2>
<p>
-前回四角形で行っていた衝突判定とその処理を今回は円でした。衝突の判定は二つの円の中心間の距離と、各円の半径の和を比較するだけなので簡単である:
-</p>
-<pre><code>struct circle {
- float ppx, ppy; // previous position (center)
- float px, py; // current position (center)
- float vx, vy; // velocity
- int r; // radius
- int m; // mass
-};
+少し前にも里芋にセスジスズメがついていたが、気づいたら一匹もいなくなった。その前にはニンジンについたキアゲハが一晩でいなくなった。犯人はこいつか。</p>
+]]></description>
+</item>
+<item>
+<title>サマータイムレンダ読んだ</title>
+<link>https://www.mtkn.jp/journal/posts/20230803.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20230803.html</guid>
+<pubDate>Thu, 3 Aug 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>サマータイムレンダ読んだ</h1>
+<time>2023-08-03</time>
-int
-circle_test_collision(struct circle *c1, struct circle *c2)
-{
- return (c1->px - c2->px) * (c1->px - c2->px) +
- (c1->py - c2->py) * (c1->py - c2->py) <
- (c1->r + c2->r) * (c1->r + c2->r);
-}
-</code></pre>
+<p>
+サマータイムレンダを読んだ。弟が漫画を買ってリビングに置いて母や妹に読ませていた。ある日なにげなくパラパラめくり始めたのだが、気づけば全部読んでしまっていた。13巻で完結するコンパクトな漫画である。</p>
<p>
-衝突後は前回と同じく弾性衝突として処理した。四角形とは違い、衝突方向の場合分けが不要なので楽である。
-</p>
-<pre><code>
-void
-circle_handle_collision_mm(struct circle *c1, struct circle *c2)
-{
- if (!circle_test_collision(c1, c2))
- return;
+話の内容はサスペンスとしては面白かった。サスペンス自体を論評する知識はないが、少なくとも僕は楽しめた。話の構成とその展開が上手いのだと思う。</p>
- float col_px = c2->px - c1->px;
- float col_py = c2->py - c1->py;
- float col_pr = sqrtf(col_px * col_px + col_py * col_py);
- col_px /= col_pr;
- col_py /= col_pr;
+<p>
+ところが最後の方に来て少し残念な感じになった。展開はそれまで通りのものに加え、クライマックスの勢いが付いているのだが、黒幕の動機がどうもうすっぺらい。そんな幼稚なやつにふりまわされていたのかと思うと、話の展開が面白いだけになんとも空虚になる。300年前に生まれた神職がそんなこと考えるのかも疑問である。そして動機がただ利己的であるがゆえに、悪役がどこから見てもただの悪役でしかない。だからこそ結末はこの悪役を倒してめでたしめでたしとしかならない。その上途中でおこった取り返しの付かないはずのことも、結局うまいことひっくり返して何も失わずにハッピーエンドを迎えた。これもあまり好みではなかった。</p>
+<p>
+この辺に関して個人的には最近読んだ「風の谷のナウシカ」のコミックがよかった。こちらには完全な悪がでてこない。敵であってもそれぞれなにかしらの事情があり、それぞれの信念に基いて行動している。だからこそ悪を殲滅して終りにはならないし、敵に勝ってもやるせない。このすっきりしなさが物語に重層性を持たせているように思う。さらに起ったことは事実として定着し、覆ることがないというのも重要なテーマとしていたように思う。どんな過去であっても今の自分たちの生命を以って未来を切り開くというものである。題材も内容も全く違う作品なので比べてもしょうがないが。</p>
- c1->px = c1->px - col_px / 2;
- c1->py = c1->py - col_py / 2;
- c2->px = c2->px + col_px / 2;
- c2->py = c2->py + col_py / 2;
-}
+<p>
+僕はそもそも死んで時間が戻るのは好きではない。人生の一回性が無くなってしまうからだ。しかしこの作品は死んで時間が戻るといっても、一応完全には取り返しの付かないようになっている。それもあって楽しめたのだが、最後の最後で全部なかったことにしてしまったのは残念である。</p>
-void
-circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
-{
- if(!circle_test_collision(c1, c2))
- return;
+<p>
+それから、時間が戻る際に主人公は意識が連続しているので、完全に同一人物といえるかもしれないが、その他の登場人物はどうなのか。戻る前と戻った後では同じ意識の人間なのか。主人公から見れば自分以外に主観は無いので、時間が戻っても以前と同じ人間に見えるだろうが、しかしそれでいいなら本人もコピーされた影も同じだということにならないだろうか。この点について、どこかで問題提起されていたと思うが、結局回収されずにおわった。最後のハッピーエンドはあくまでも本人のみのものであり、それまでに死んだ他の登場人物は結局そのままなのではないだろうか。これもひとつの大きなテーマだと思っていただけに、どうも疑問が残る。</p>
- float col_px = c2->px - c1->px;
- float col_py = c2->py - c1->py;
- float col_pr = sqrtf(col_px * col_px + col_py * col_py);
- col_px /= col_pr;
- col_py /= col_pr;
- float nor_px = col_py;
- float nor_py = -col_px;
+<p>
+最後に、「あっぽけ」って言ってる人は見たことがない。「今日わは...」については大阪人の友達に言ってきょとんとされた経験がある。</p>
+]]></description>
+</item>
+<item>
+<title>着物できた</title>
+<link>https://www.mtkn.jp/journal/posts/20230802.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20230802.html</guid>
+<pubDate>Wed, 2 Aug 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>着物できた</h1>
+<time>2023-08-02</time>
- float m1 = c1->m;
- float m2 = c2->m;
+<p>
+作っていた着物が完成した。思っていたよりもずっと簡単だった。細かいところを追求すれば職人技なのだろうが、日常的に使うものを作るには素人でも十分である。</p>
- float col_1v = c1->vx * col_px + c1->vy * col_py;
- float col_2v = c2->vx * col_px + c2->vy * col_py;
+<p>
+初めは白衣のつもりで安物の晒をそのまま使って作り始めたが、白衣を着ることが無くなってしまったので、袖と衿だけ柿渋で染めて襦袢にすることにした。形は長着だが。</p>
- float col_1vxn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_px;
- float col_1vyn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_py;
- float col_2vxn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_px;
- float col_2vyn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_py;
+<p>
+完成後、右の衿がかなりずれて付いていることに気付いた。このときは疲れからか不幸にもこのままでいいやと思っていたが、見れば見るほど気になって、とうとうこの部分だけ付けなおすことにした。そして完成したのがこれである。</p>
+<img src="20230802.jpg" alt="初めて自作した着物の写真" />
- float nor_1vx = nor_px * (c1->vx * nor_px + c1->vy * nor_py);
- float nor_1vy = nor_py * (c1->vx * nor_px + c1->vy * nor_py);
- float nor_2vx = nor_px * (c2->vx * nor_px + c2->vy * nor_py);
- float nor_2vy = nor_py * (c2->vx * nor_px + c2->vy * nor_py);
+<p>
+ほんのひと昔前までは着物を作る人などその辺にいくらでもいた。世話になっていた寺の住職のお婆さんは和裁の先生だったそうだし、僕の祖父母より少し若いお茶屋さんは、自分のお母さんが家族の着るものを自分で作っていたという。つまり僕の3、4世代前は着物を自作するのが当たり前だった。ところがこの50年程の間にだれも作らなくなってしまった。和服に限らず自分で作れば安上りなのに。</p>
- c1->vx = col_1vxn + nor_1vx;
- c1->vy = col_1vyn + nor_1vy;
- c2->vx = col_2vxn + nor_2vx;
- c2->vy = col_2vyn + nor_2vy;
+<p>
+なぜ自作しないのかと聞くと、きっと「そんな時間はない」という答えが返ってくる。金銭的にゆたかになって、自分で服を作る必要がなくなった。のではなく、服を作るような時間的余裕のある人がいなくなったというのが実情である。はて。</p>
+]]></description>
+</item>
+<item>
+<title>無題</title>
+<link>https://www.mtkn.jp/journal/posts/20230729.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20230729.html</guid>
+<pubDate>Sat, 29 Jul 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>無題</h1>
+<time>2023-07-29</time>
+<p>
+最近持っている浴衣がどれもぼろぼろになってきた。元々古いもらいものなうえ、着方も動き方もなってないので着ているとすぐにどこか破れる。補修しながら使っていたが、いよいよ布自体がすり切れてきた。いい機会なので自分で仕立ててみようと思いたった。
+実は寺に居たころに白衣を作ろうと思い、晒の裁断と袖の縫いまでしていた。ほったらかしになっていたこれを再開することにした。</p>
+<p>
+腋縫いまでしていたこの着物に、今日はおくみを片方だけ付けた。両方したかったが疲れたのでもう片方はまた今度。色が付いていないただの晒だが、白衣など着る機会が無くなってしまったので、袖と襟だけでも染めることにした。家にころがっていたターナーの無臭柿渋を3倍に薄めて染めた。本当は自分で育てた柿を使いたいが、そんん場所はないので買ってきた渋である。柿渋は紫外線で硬化して定着するらしく、媒染剤は必要ないようである。襦袢くらいには使えるだろうか。</p>
- circle_handle_collision_mm(c1, c2);
-}
-</code></pre>
+<p>
+梅を塩に漬けているのを思いだしたのでこれを庭に干した。県民の義務である。そして今日は土曜である。本当は自分で育てた梅を使いたいが、そんな場所はないので買ってきた梅である。残った梅酢は生姜でも漬けようか。</p>
-<h2>完成品</h2>
<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex5/ex5.c.html">git</a>
-</p>
+庭に植えた里芋から芽がでてきた。大野芋というものであるが、僕が住んでいる場所とは関係がない。既に大きな葉が付いていたのだが、セスジスズメという芋虫にやられて禿げていた。四匹いたのだがある晩二匹減り、二日後にもう一匹、そしていつのまにか最後の一匹が居なくなっていた。なにかに食べられたのか。蛹は見ていない。以前ニンジンの花がキアゲハにやられた時も、たくさん居た芋虫が一晩で一匹も居なくなっていた。ところで芋虫というのは見ているとなんだか美味しそうに見えてくる。実際美味しいらしい<sup>[1]</sup>が。まだ食べる勇気はない。</p>
<p>
-<video controls>
-<source src="videos/ex5.webm" type="video/webm">
-</video>
+この里芋は葉だけでなく茎を根本までかじられて切り株のようになっていたが、その切り株の中心が少しづつ盛り上がってきている。芋は強い。
</p>
-<h2>参考</h2>
<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+<li>[1]<a href="https://survivalnature.sakura.ne.jp/0000/11_mushi/imomushi/0000.html">イモムシ・ケムシ.サバイバル節約術</a></li>
</ul>
+
+]]></description>
+</item>
+<item>
+<title>GOPL読んだ</title>
+<link>https://www.mtkn.jp/journal/posts/20230726.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20230726.html</guid>
+<pubDate>Wed, 26 Jul 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>GOPL読んだ</h1>
+<time>2023-07-26</time>
<p>
-次の記事: <a href="xlib_playground6.html">Xlibで遊んでみる6</a>
-</p>
+Alan A. A. DonovanとBrian W. Kernighanの「The Go Programming Language」を読了した。読み初めた日付は記録していないが、演習問題用のgitリポジトリは、今年の5月19日が最初のコミットである。二ヶ月ちょっとかかった。本文だけでなく演習問題も濃かった。時間をかけて読む価値があると思う。Kernighanの書いた本は英語が綺麗でいい。</p>
+<p>
+アマゾンで英語の原著を買ったが、表紙の紙のコーティングみたいなビニールが剥れてきているのが残念。</p>
]]></description>
</item>
<item>
-<title>Xlibで遊んでみる4</title>
-<link>https://www.mtkn.jp/computer/xlib_playground4.html</link>
-<guid>https://www.mtkn.jp/computer/xlib_playground4.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる4</h1>
-<time>2023-01-02</time>
+<title>水道と空調</title>
+<link>https://www.mtkn.jp/journal/posts/20230725.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20230725.html</guid>
+<pubDate>Tue, 25 Jul 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>水道と空調</h1>
+<time>2023-07-25</time>
+<h2>水道</h2>
<p>
-前回: <a href="xlib_playground3.html">Xlibで遊んでみる3</a>
+水道が普及して井戸が使えなくなった。水道があれば井戸から飲み水を汲む必要がない。井戸から飲み水を汲まなくなれば井戸水が無くなっても、あるいは汚染されても問題ない。問題なければその通りになる。水源を涵養するという考えが忘れられ、森がコンクリートで覆われた。水が汚なくても良くなったので農薬や肥料を大量に使うようになった。そして井戸は水位が下り、汚染された。</p>
+<p>
+便利な水道が普及した為に、水道が無いと不便な世界になった。不便というよりそれがないと生きるのもままならない。現在我々はそのインフラの維持費と、災害時の脆弱性に悩まされている。</p>
+<h2>空調</h2>
+<p>
+空調が普及したら気温が高くなった。逆ではない。空調があれば気温が高くなっても問題ない。問題なければその通りになる。街から植物が一掃されコンクリートとアスファルトで覆われた。暑くても問題ないので経済優先で自動車の為の道路ばかり整備された。そして街の気温は上った。</p>
+<p>
+便利な空調が普及した為に、空調が無いと不便な世界になった。不便というよりそれがないと生きるのもままならないようになりつつある。</p>
+<p>
+そのうち空調の効率を上げるために、都市全体が大きなドームに覆われる。ドームの外は50℃か60℃か。このインフラもまた維持費と災害に対する脆弱性で我々を悩ませることになろうか。</p>
+]]></description>
+</item>
+<item>
+<title>新型コロナウイルスとその対策に関する個人的見解</title>
+<link>https://www.mtkn.jp/journal/posts/20220730.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20220730.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>新型コロナウイルスとその対策に関する個人的見解</h1>
+<time>2022-07-30</time>作成<br>
+<time>2022-08-03</time>更新<br>
+<h2>はじめに</h2>
+<p>
+新型コロナウイルスとその対策に関していろいろと思うことはあるが、自分のなかでもあまりまとまっていないうえ、前提となる知識や情報、考えの論理的整合性がとれていない部分が多々ある。それもあってあまり文章として僕の考えを書こうとは思っていなかった。しかし今回の感染症が流行りだして2年と半年が経ち、いまだに社会全体がおかしい(と僕が思う)状態である。そのため自分も黙っているのはむしろ無責任ではないかと思いここに書き残すことにした。どうせほぼ見てくれる人はいないだろうけれど、それでもなにかしたかった。
</p>
<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+僕自身はどんなことがあろうがそれを運命と呼び、それを受け入れる覚悟である。その方が日々楽しく生きて納得の上で死んで行けるように思うのだ。自分でも素人なりに論文を読んだりして可能な限り信頼性の高い情報を集めようとはしているのだが、このような考えが根底にあるので、科学的な根拠というのはなんだかどうでもよく思えてきたりするもので、どうも今持っている知識というのがふわっとして正確性に欠ける。しかしだからこそ書きとめて後で反省できるようにしたい。
+</p>
+<p>
+そのためこの文章は正確な知識に基くものではない。本来論文等をきちんと引用するべきところだが、とりあえず現時点での考えとして公開することにした。また、自分では気を付けているつもりだが、知らないうちに陰謀論を掴まされている可能性もある。おかしな点は指摘してくれるとありがたい。
</p>
-<h2>衝突判定とその処理</h2>
+<h2>概要</h2>
<p>
-これまでは一つの四角形だけを描画していたが、今回は複数の四角形を作成して動かしてみた。ランダムな場所にランダムな運動量で動かして、他のものやウィンドウの縁とぶつかったら跳ね返るようにした。</p>
+マスクは今回の感染症を防ぐ効果は非常に限定的だと思う。一方でその副作用は大きい。体が常に酸素の薄い状態になっている。夏は熱中症の危険もある。却って口が乾燥するという人もいる。なによりの懸念は子供の発育への影響である。他人の表情が見えなくてどうしてまともに育つのか。
+</p>
<p>
-回転しない四角形どうしの衝突判定は簡単である。x軸方向とy軸方向の両方に重なりがあれば衝突している:
+ワクチンについても効果は薄く、副反応が大きいので打つべきではないと思う。特に若年層は発症した際のリスクが低く、逆にワクチンによるものと思われる心筋炎等の可能性が高い。さらに打ったからといって感染する確率は変らないようだ。
</p>
-<pre><code>struct square {
- float ppx, ppy; // previous position
- float px, py; // current position
- float vx, vy; // velocity
- int w, h; // width and height
-};
-
-int
-test_collision(struct square *s1, struct square* s2)
-{
- return s1->px < s2->px + s2->w && s2->px < s1->px + s1->w &&
- s2->py < s1->py + s1->h && s1->py < s2->py + s2->h;
-}
-</code></pre>
-
<p>
-衝突後の処理は多少めんどくさかった。衝突した時は既にめりこんでいるので、まずはそれぞれをめりこんだ距離の半分ずつずらして衝突を解消するようにした。この際、x軸方向にぶつかったのか、y軸方向にぶつかったのかで、それぞれの軸方向にひっぺがすようにしている。二つの四角形の各軸に関するめりこんだ距離<code>lapx</code>、<code>lapy</code>と各軸に関する相対速度<code>rel_vx</code>、<code>rel_vy</code>の比を比べればどちらの軸方向にぶつかったかが分かるはずである、多分 :
+消毒もやらない方がいいと思う。
+</p>
+<p>
+またこの病気自体危険性がそこまで高くないので、そもそも感染の拡大を防ぐ必要はないと思う。
+</p>
+<p>
+さらに、例え危険な病気が流行したとしても、人間味のある生活を犠牲にすることのデメリットとその病気の危険性とを天秤にかけ、どのような対策が適正なのかをきちんと議論すべきである。
</p>
-<pre><code>void
-handle_collision_mm(struct square *s1, struct square *s2)
-{
- if (!test_collision(s1, s2))
- return;
-
- float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px);
- float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py);
- float rel_vx = max(s1->vx - s2->vx, s2->vx - s1->vx);
- float rel_vy = max(s1->vy - s2->vy, s2->vy - s1->vy);
-
- if (lapx / rel_vx < lapy / rel_vy) {
- if (s1->px + s1->w < s2->px + s2->w / 2) {
- s1->px -= lapx / 2;
- s2->px += lapx / 2;
- } else {
- s1->px += lapx / 2;
- s2->px -= lapx / 2;
- }
- } else {
- if (s1->py + s1->h < s2->py + s2->h / 2) {
- s1->py -= lapy / 2;
- s2->py += lapy / 2;
- } else {
- s1->py += lapy / 2;
- s2->py -= lapy / 2;
- }
- }
-}
-</code></pre>
<p>
-衝突は弾性衝突として、衝突したそれぞれの四角形の速度を更新した。質量は四角形の面積として計算している。衝突後の速度はエネルギー保存則と運動量保存則から導いたのでしんどかった。
+ところがそのような冷静な考察をする上で必要な情報がなにひとつみあたらない。感染者数や死者数として発表される数字は統計として信用できないだけでなく、医療機関に対する補助金等により大きく歪められている可能性が高い。
</p>
-<pre><code>void
-handle_collision_elastic(struct square *s1, struct square *s2)
-{
- if(!test_collision(s1, s2))
- return;
-
- float v1, v2;
- float m1 = s1->w * s1->h;
- float m2 = s2->w * s2->h;
-
- float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px);
- float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py);
-
- if (lapx < lapy) {
- v1 = s1->vx;
- v2 = s2->vx;
- s1->vx = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1;
- s2->vx = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2;
- } else {
- v1 = s1->vy;
- v2 = s2->vy;
- s1->vy = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1;
- s2->vy = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2;
- }
-
- handle_collision_mm(s1, s2);
-}
-</code></pre>
-
-<h2>サブティック</h2>
<p>
-この名前が適切かどうか分からないが、前のフレームから次のフレームまでの時間をさらに何等分かして衝突判定の制度を上げた(マクロは括弧でかこって分かりにくいバグを防げとどこかに書いていたのでそうすることにした):
+また、マスクやワクチン等は同調圧力により行っている人が多いと思う。周囲の人間がやっているからといった理由だけでの行動である。このような世間の流れは非常に危険だと思う。今回は軽い風邪でしかなかったが、この程度のことでここまで社会全体がひとつの空気に支配されているようでは、本当に国運を分けるようなことに見舞われればひとたまりもない。意見の多様性を無視してひとつの流れを作ってしまうと、その流れが間違っていたときに国が滅びるだろう。
</p>
-<pre><code>#define SUB_TIC (4)
-
-void
-game_play(void)
-{
- /* ... */
- while (next_menu == GAME_PLAY) {
- /* ... */
- for (int j = 0; j < SUB_TICK; j++) {
- for (int i = 0; i < NUM_SQUARE; i++)
- next_tick(&square[i], 1000 * 1000 * 1000 / FPS / SUB_TICK);
-
- for (int i = 0; i < NUM_SQUARE; i++)
- for (int j = i + 1; j < NUM_SQUARE; j++) {
- handle_collision_elastic(&square[i], &square[j]);
- /* ... */
- }
- /* ... */
- }
- /* ... */
- }
- /* ... */
-}
-</code></pre>
-
-<h2>完成品</h2>
<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex4/ex4.c.html">git</a>
+しかしやはり一番心配なのは今このような環境のなかで自我を形成している子供である。マスクやワクチンはそれ自体が非常に強く影響する上、同調圧力も重くのしかかる。こんな状態で子供が健全に育つとはとても考えられない。10年、20年あるいはもっと先かも知れないが、今の子供達がこの国を動かすようになれば必ず大きな問題があちこちで噴出することになるだろう。
</p>
<p>
-<video controls>
-<source src="videos/ex4.webm" type="video/webm">
-</video>
+そもそもウイルスは敵ではない。生態系の重要な一員である。共存していけばいいのだ。
</p>
-<h2>参考</h2>
-<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-</ul>
+<h2>マスクについて</h2>
<p>
-次の記事: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
+一応いろいろな論文を読んだ。マスクの効果に肯定的なものも否定的なものもあった。僕が読んだものはTwitterで拡散されているものが中心であるが、そのなかでマスクの効果に肯定的なものは、電話やメール等による調査とシミュレーションによるものばかりだった。前者は論文の中でも明記されているが、回答するかしないかは回答者に委ねられており、また回答者のバイアスもあり、とても信頼できるものとは言えないと思う。後者についてはマスクには定性的には効果があるとの前提に立って、その効果を定量的に測るものであるため、こちらも本当に意味があるものかどうか相当あやしいと思う。日本においてもスーパーコンピュータの富嶽によるシミュレーションを何度も目にしたが、これもマスクにより飛沫が防げると言っているだけで、だからウイルスに対してどう影響があるかという点については説明していない。マスクをすれば飛沫が飛びにくくなるのはスーパーコンピュータで計算するまでもなく理解できることではないか。一方でマスクの効果に否定的な論文には、RCT等、無作為性の高い手法を用いたものが多く、こちらのほうが信頼性が高いと判断した。ところでウイルスがマスクの繊維の隙間の大きさよりも圧倒的に小さいのでマスクに効果がないと言う人もいるがこれについてはなんとも言えないと思っている。マスクの穴が大きくても、例えば繊維に静電気で吸着されたりするかもしれない。このような可能性だけで議論すればいつまでも答えがでない。やはり信頼できる根拠というのは現実の観測であって無作為性の高い統計的手法によるものだと思う。
+</p>
+<p>
+続いてマスクの害について、先日マスクを着用しないと参加できないという場にどうしても行く必要があり、久々に着用した。あまりにも苦しかった。自分が喘息を持っているということもあるかもしれないが、明かに脳に届く酸素が少いと思う。これはバイアスかなあ。このような状態で2年以上も過している人の脳にはなにかしらの障害が起きていてもおかしくないと思うのだが。あるいはマスクにより口呼吸になり却って喉が乾くという話もある。喉が乾けばウイルス等に対する防御も手薄になるだろう。また不織布マスクからはマイクロプラスチックが放出され、それを大量に吸い込み続けているという話も聞く。マイクロプラスチックを吸い込むことと肺癌とを結び付ける話もあるがこの辺はあまりきちんと確かめていない。他にもいろいろなリスクがあるだろうが、一番重大なものはやはり子供の発育である。生れたときから周囲の人間がマスクをした状態で、どうして人間の表情や感情、あるいは言語を学ぶというのだろう。生まれたての赤ちゃんでなくても、他人との人間関係を学び自我を形成している最中の子供達にとって、周囲の表情が分からないのはあまりにも影響が大きいのではないだろうか。大人でもマスクをした他人の表情を読みとるのはマスクをしていない場合よりも難しいが、子供の場合はそれがさらに顕著であるという。このように社会全体が長期に亙ってマスクで顔を覆っていては、今の子供達の世代が将来酷く歪んだものになると思う。これに関して警鐘を鳴らしている人はいるが、社会全体では無視されている。これには科学的根拠はあまりないようだ。結果が出るのが数十年後であろうから当然である。しかしだからといって子供を虐げていい理由にはならないだろう。子供がまともに育たずに害を被るのは彼らが成長したときに年寄になっている今の我々自身かも知れない。ところで徳川時代において、徳川家の子供は生まれた時から尊いというので、乳母は母乳を与える際に顔を見せないようになにか被っていたらしい。それとの因果関係は不明だが、徳川家の子供は当時の平均よりも死亡率が高かったという。この話もきちんと確かめなければ。一方マスクのせいで子供の方が表情を作れなくなるのではないかと考えていたこともあるがこれについてはどうやら心配ないかもしれない。チンパンジーかなにかの実験で、子供の時から他の個体の表情を見せないように育てても、本人は表情を作ることに障害はなかったそうだ。表情の作り方は本能に実装されているらしい。
</p>
-]]></description>
-</item>
-<item>
-<title>Xlibで遊んでみる3</title>
-<link>https://www.mtkn.jp/computer/xlib_playground3.html</link>
-<guid>https://www.mtkn.jp/computer/xlib_playground3.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる3</h1>
-<time>2023-01-02</time>
+<h2>ワクチンについて</h2>
<p>
-前回: <a href="xlib_playground2.html">Xlibで遊んでみる2</a>
+ワクチンの効果は非常に限定されているようである。当初は一回打てば感染しなくなり、今回の騒動も収まると言っていたように思う。当時からそんな訳ないやろとは思っていたが、案の定全く収束する気配はない。二回目には打てば重症化しなくなり、感染の拡大も終わると言い、三回目もこれで終りだと言っていた。そして今は四回目である。僕は一回も打っていないが、これまで政府の言うことを信じて打って来た人は未だに感染の拡大が収まらないことをどのように考えているのか不明である。更に四回目を打てと言われれば未だにその指示に従う理由もよく分からない。
</p>
<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+また副反応も大きいではないか。いちいち高熱がでて苦しんでいる人が沢山いる。僕の父も三回目にやたら高熱がでて仕事の最中に会社から帰って来て家で寝ていた。心筋炎の危険も高いようである。政府がデータをちょろまかしていたと言う話があったが、ワクチンを打った場合と打たなかった場合では打った場合の方が圧倒的にリスクが高いようだ。そもそも今回のワクチンは従来のものとは根本的に違う新しいものであり、その安全性も未知数である。特に長期的な影響についてはなにも分かっていないのではないか。もちろん新しい技術には常に危険が付いてまわるものである。それを理解したうえで打ちたいと言う人は本人の責任で打てばいい。ただし今回のように国民全員に打たせようというのは間違いである。こんなことをしてしまっては万が一この新しい技術に長期的な危険性があった場合に全国民がその影響を受けることになる。卵はひとつの籠に入れて運んではいけないのだ。
</p>
-
-<h2>画面サイズの変更</h2>
<p>
-画面サイズが変更された時に表示している四角形が画面の外側に出ないようにした。<code>XGetWindowAttributes()</code>で画面の情報を取得し、グローバル変数の<code>win_width</code>と<code>win_height</code>に幅と高さをそれぞれ代入して<code>next_tick()</code>で四角形の位置を画面に収まるようにしている:
+ところでワクチンを打つのが無料だと思っている人が多いようだがそんな訳ないやろ。全部国の金であり、つまりは我々の税金である。国債でも発行してくれるのならまだいいが、東日本大震災の前例があるのでどうせ増税されるに決まっている。その時はぜひともワクチンを打った人にのみ課税してもらいたいものだ。僕は払いたくない。また万が一このワクチンに長期的な後遺症等の危険があることが判明でもすれば、その治療にも国民健康保険という名の税金が使われるのだろうと思うとやるせない。
</p>
-<pre><code>int win_width, win_height;
-
-void
-receive_events(int key_state[])
-{
- XEvent event;
- XWindowAttributes wattr;
-
- while (XPending(display) > 0) {
- XNextEvent(display, &event);
- switch (event.type) {
- case Expose: {
- XGetWindowAttributes(display, window, &wattr);
- win_width = wattr.width;
- win_height = wattr.height;
- } break;
- /* ... */
- }
- }
-}
-
-void
-next_tick(long ndt) // nano second
-{
- px = px + vx * ndt / 1000 / 1000 / 1000;
- py = py + vy * ndt / 1000 / 1000 / 1000;
- // bind within the window
- if (px < 0)
- px = 0;
- if (win_width < px + width)
- px = win_width - width;
- if (py < 0)
- py = 0;
- if (win_height < py + height)
- py = win_height - height;
-}
-</code></pre>
-<h2>メニュー画面の実装</h2>
+<h2>消毒について</h2>
<p>
-ゲームのようなものを作るうえでメニュー画面とその推移が必要である。ここではグローバル変数<code>next_menu</code>に現在のメニューを保存することにした。それぞれのメニューはそれぞれ関数として記述し、他のメニューに推移する必要が生じたときに<code>next_menu</code>を変更するようにした:
+消毒についても意味がないだけでなく逆効果である可能性もあると思う。手指消毒に関しては、一般に行われている程度のものではウイルスが死滅するとは思えない。ウイルスが死滅する程の消毒をいちいち行っていては、常在菌をも殺してしまい、さらに皮膚に備わっている物理的な免疫機構も働かなくなると思う。結局これも対策をしているというアピールの為のものでしかないのではないか。
</p>
-<pre><code>enum next_menu {
- START_MENU,
- GAME_PLAY,
- GAME_OVER,
- QUIT,
-};
-
-int next_menu = START_MENU;
-void
-start_menu(void)
-{
- XEvent event;
- char *menu_char_q = "press q to quit.";
- char *menu_char_s = "press <space> to start.";
-
- XClearArea(display, window,
- 0, 0, // position
- win_width, win_height, // width and height
- False);
- XDrawString(display, window, gc,
- win_width/2 - strlen(menu_char_q)/2, win_height/2,
- menu_char_q, strlen(menu_char_q));
- XDrawString(display, window, gc,
- win_width/2 - strlen(menu_char_s)/2, win_height/2 + 20,
- menu_char_s, strlen(menu_char_s));
+<h2>新型コロナウイルス感染症の危険性について</h2>
+<p>
+この病気はそんなに危険なものであるとは思わない。死亡も重症化もそんなに数が多くない。また先日厚生労働省のウェブページのデータから計算してみると死者の平均年齢が男性で79歳程度、女性で83歳程度と、ほとんど平均寿命に近いものになっている。これは病気で死んでいるというよりも寿命ではなかろうか。ただしこの計算に使ったデータは年齢が10歳区切りであるのであくまでも概算である。病院がパンクしそうだという話も、どうやら軽傷の人が大量に流れこんできていて大変だということらしい。政府がこの感染症の扱いを5類に引き下げれば済む話のようだ。
+</p>
- while (next_menu == START_MENU) {
- XNextEvent(display, &event);
- switch (event.type) {
- case Expose: {
- XDrawString(display, window, gc,
- win_width/2 - strlen(menu_char_q)/2,
- win_height/2,
- menu_char_q, strlen(menu_char_q));
- XDrawString(display, window, gc,
- win_width/2 - strlen(menu_char_s)/2,
- win_height/2 + 20,
- menu_char_s, strlen(menu_char_s));
+<h2>人間味のある生活とのおりあいについて</h2>
+<p>
+今回の騒動では強権的な対策が次々と打ち出された。マスクやワクチンの推奨は半ば強制力を伴ったものとなり、飲食店の営業自粛では法的拘束力が無いにしても、従わない店は名前を公開するなどされていた。個人的な行動も同様であった。それまであたりまえに行われてきた人間らしい生活が奪われた形になる。その後、因果関係は分からないが、自殺者が大きく増えたようだ。人間が行うことには必ず負の面がある。個人の自由を制限することにはそれなりのコストが伴うのだ。危険な感染症が流行したからといってただちに人権を制限することになりかねない今回のような対策をするのは間違っている。この対策により発生する種々のコストと、対策をしなかったときの危険を天秤にかけて考える必要があろう。命を守る為と言って一切の楽しみを奪われたのでは今すぐ死んだ方がましである。あるいは命を最優先にするにしても、その命の中には当然対策をしたがために自殺に追い込まれる人も入っているはずである。国という大きな組織を運営していく上ではなんらかの数字を見て次の行動を判断せざるを得ない訳だが、その数字として死者数を考えた場合でも、対策をしないがために死ぬ人の数と対策をしたために死ぬ人の数の双方を考慮せねばなるまい。
+</p>
- } break;
- case KeyPress: {
- switch (XLookupKeysym(&event.xkey, 0)) {
- case 'q':
- next_menu = QUIT;
- break;
- case ' ':
- next_menu = GAME_PLAY;
- break;
- default:
- break;
- }
- } break;
- case ClientMessage: {
- if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
- next_menu = QUIT;
- }
- } break;
- default:
- break;
- }
- }
-}
+<h2>必要な情報の欠如について</h2>
+<p>
+上のような冷静な議論に必要なのが正確な情報である。感染症によって何人死んだのか、何人がどれほどの後遺症に苦しんでいるのか、逆に何人が感染症対策の為に死んだのか等の情報である。ところが今得られるそのような情報にはどれも正確さが欠如している。感染者数として発表されているのは無作為ではない方法でPCR検査をして陽性になった人の数である。東京都ではPCR検査を行う施設に補助金が出るので、接種を受ける人に商品券を配って集客している所もあると言う。そもそもPCR検査の開発者であるキャリー・マリス氏はこの検査は感染症の診断に利用すべきでないとの見解である。またこの感染症による死者数として発表されているのはあらゆる原因の死者のうちPCR検査により陽性だった人の数である。別の原因で死んでも陽性であれば新型コロナウイルス感染症による死者である。更に、新型コロナウイルス感染症により患者が死亡すればその病院に補助金が出ると言う話もある。結局この感染症の本当の姿というのは一切見えてこない。テレビ等ではひたすら上のような定義の感染者数ばかり報道して視聴者の恐怖を煽っているだけである。ただでさえいい加減な定義の数字をさらに補助金によって歪めているのが現状である。このような状況で市井の人々は一体なにを怖がっているのか不明である。
+</p>
-int
-main(void)
-{
- setup();
- while (next_menu != QUIT){
- switch (next_menu){
- case START_MENU:
- start_menu();
- break;
- case GAME_PLAY:
- game_play();
- break;
- case GAME_OVER:
- game_over();
- break;
- default:
- break;
- }
- }
+<h2>同調圧力について</h2>
+<p>
+このような状況において、結局人々はどうしていまだにマスクをしてワクチンを打つのかといえば、それは同調圧力によるものである。もちろん一部にはこのウイルスを本当に恐れている人もいるだろうが、僕の周りではかなり少数派である。むしろ周りの目を気にしている人が大多数だろう。あるいは単に世の中の流れだからという理由でそれに付き従っている人も少なくない。中には自分が苦しい思いをしてマスクを着けているのだからお前も苦しいのを我慢して着けろと言う人や、周囲が常識として受け入れていることなのだから自分もしないと恥をかくと言う人もいる。同調圧力を感じてそれに屈っしてマスクを着用している人も居るが、それでは結局自分自身がその同調圧力になり、それを維持してしまっているではないか。このようなことでは結局いつまでたってもやめられない状態に陥ってしまう。というか陥っている。
+</p>
+<p>
+僕は上に書いた通りマスクは効果も必要性もなく害があると思っているので着用しない。この考えが本当に正しいかどうかは正直よく分からないが、少なくともマスクを着ける効果や必要性がないと思っているのであれば周りの目など気にせずにその通り行動するべきだと思う。同調圧力に弱い人というのはおそらく社会の流れに合わせてさえいればその流れが間違っていても自分には責任が生じないと考えているのだろう。ところで僕は今のようにマスクを強要するような状態が長く続けば必ず取り返しの付かないような問題がでてくると思っている。特に子供は同調圧力による影響を受けやすいように思う。学校でマスクをしなかったりワクチンを打っていなかったりしていじめられるという話も聞く。彼らは大人になっても周囲を伺うばかりで何一つ判断できない人材になるだろう。このような人達が国を運営する時代がいずれ来る。そのような社会で老後を過ごさなければいけないのは結局この社会の流れに同調していた人自身である。社会の趨勢に任せておけば確かに明確に責任を追求されることはないかも知れないが、その結果の尻拭いをするのは自分自身なのだ。
+</p>
+<p>
+国を形作っているのは自分達自身である。国民とは別に政府が存在するのではなく、政府は国民自身が作るものである。個人とは別に社会の流れがあるのではなく、自分達が社会の流れそのものなのである。自分の頭で考え自分が信じたことを行動で周囲に示すのはむしろ社会に生きる一個人としての責務ではなかろうか。選挙で投票するのは国民の責務だというのと同じである。
+</p>
- cleanup();
- return 0;
-}
-</code></pre>
-<p><code>main()</code>関数がめっちゃすっきりした。</p>
+<h2>意見の多様性について</h2>
+<p>
+同調圧力と関連するが、社会をいい方向に進める上で重要なのが意見の多様性である。ここに書いていることは僕個人の考えであり、それが正しいものかどうかはよく分からない。科学者の中にもマスクやワクチン等に関して種々の意見があり、統一されているものではない。そもそも科学は不完全なものなので科学者の意見が統一されてもそれが正しいかどうかはっきり言えるものではないだろう。個人の信条の問題も絡むといよいよ統一した見解というものを出すのは不可能である。しかしこのような意見の多様性というのがむしろ重要なのではないだろうか。多様な意見があればそのなかで最もいいものが生き残っていく。我々人間を含む生物というのはこのようにして進化し、生き残ってきたのではないだろうか。その結果が混沌のなかにある種の調和がとれたこの大自然というものであろう。
+</p>
+<p>
+ところが今のように一方の意見に統一して他方を無視し、あるいは排除しようとするのはあまりにも危険である。もし社会が選んだ方が間違っていたら人類は絶滅である。卵はひとつの籠に入れて運んではいけない。まあそのような世界になるのもひとつ楽しみであるかも知れないが。
+</p>
-<h2>完成品</h2>
+<h2>子供について</h2>
<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex3/ex3.c.html">git</a>
+しかしなんといっても最優先して考えるべきは子供の未来ではないだろうか。今の感染症対策は結局のところ年寄の命を守る為に子供の生活を犠牲にしているというものである。別に対策などしなくてもこの病気で死ぬ年寄はこの病気でなくても死ぬだろうと僕は思うが、そうでなくてもこの感染症の死者および重症患者はやはり高齢者が中心であろう。そして高齢者を守る為であれば子供の生活や成長が犠牲になるということなど議論の俎上にも登らない。どのみちそう長くない高齢者の命と、この先国の中心を担っていく現在の子供達の生活や成長とどちらが大事なのかは考える余地もないと思うのだが現状そんなことを言っている人は少ないようである。今子供達の生活を鑑みずに自分達の命を最優先に考えて行動している年寄は恥を知れ。
+</p>
+<p>
+子供への悪影響は多岐に渡る。マスクやワクチン、消毒等の直接の影響と、これらすべてに関連した同調圧力による影響である。これらは各項目の所で書いたのでここでは繰り返さない。
</p>
+
+<h2>ウイルスは敵ではない</h2>
<p>
-<video controls>
-<source src="videos/ex3.webm" type="video/webm">
-</video>
+思うにウイルスは人類の敵ではない。彼らに感染することで他種の生物と遺伝情報のやり取りができると聞く。人間は人間だけで自然界を生きている訳ではない。互いに影響しあい、時には殺し合い、あるいは寄生しあい、生きているものである。そうしてあらゆる生物がその総体としてひとつの生態系を形成しているのだと思う。人間の命を最優先にして他の生命を排除するようではそのうち生態系の方から人類が見捨てられることにもなるだろう。ウイルスの方は我々と共存する道を探りながら進化している。人間の方もそれに応えるべきだ。
</p>
-<h2>参考</h2>
-<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-</ul>
+<h2>最後に</h2>
<p>
-次の記事: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
+ここに書いたことは決して正確なことではない。僕の持っている科学的な知識や情報もいい加減なものである。ただし自信を持って言えるのは、僕は自分の頭で考えその結果正しいと思った行動を取っているということである。僕の考えが間違っていても、それは自分が正しいと思い、その通りに行動しているので、後で謝ることもできるし反省することもできる。自分で考えず、あるいは間違っていると思うことをしていては、後になって反省も謝罪もできない。そこにあるのは周りがやっていたから合わせただけで自分には責任がないという言い訳だけだ。しかしその結果どのような社会になろうが、自分もそこに住むことになるのだ。口で責任が無いとは言っても、結局その尻拭いは自分自身ですることになるのだ。今きちんと考えて自分の責任で行動しておかないと自分が死ぬ時にどんな顔をすればいいのか分からないだろう。食べてしまったケーキはとっておくことができないのだ。</p><div style="display: none;">別に爆弾は作らない。</div>
+<p>
+僕は他人に言われた通りやってつまらない人生を送るより、死ぬ危険があっても自分の信じたことをして生きたい。その方が死に直面したときに気分よく死ぬことができると思う。
</p>
]]></description>
</item>
<item>
-<title>Xlibで遊んでみる2</title>
-<link>https://www.mtkn.jp/computer/xlib_playground2.html</link>
-<guid>https://www.mtkn.jp/computer/xlib_playground2.html</guid>
+<title>x220から投稿テスト</title>
+<link>https://www.mtkn.jp/journal/posts/20210106.html</link>
+<guid>https://www.mtkn.jp/journal/posts/20210106.html</guid>
<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる2</h1>
-<time>2022-12-22</time>
-
-<p>前回: <a href="xlib_playground1.html">Xlibで遊んでみる1</a></p>
-<p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。
+<description><![CDATA[<h1>x220から投稿テスト</h1>
+<time>2021-01-06</time>
+<p>
+thinkpad x220を手に入れ、arch linuxの環境を整えた。
</p>
-
-<h2>FPSの固定</h2>
-<p>前のフレームからの経過時間を計測して<code>1.0/FPS</code>を越えるまで待機させる。このときに<code>nanosleep()</code>を使うとなぜか上手くいかなかった。ナノ秒単位で処理できそうな名前なのに使えない。多分OSのコンテクストスイッチがどうとかいう話やと思う。知らんけど。組み込みとかで使うんかな?
+<p>
+スペックはこんな感じ:
</p>
-
+<ul>
+ <li>core i7 2620M</li>
+ <li>メモリ 8GB</li>
+ <li>ssd 256GB。</li>
+</ul>
<p>
-とりあえず<code>while</code>ループの中でひたすら時刻を読んでいる。リソースの無駄遣いではないのだろうか:
+ヤフオクで送料入れて15000円程度だった。
</p>
-<pre><code>#defin FPS 60
-
-int
-main(void)
-{
- long t0, t1, dt;
- int fps_count;
-
- clock_gettime(CLOCK_MONOTONIC, &ts);
- t0 = ts.tv_nsec;
-
- while (!quit) {
- // fix fps
- dt = 0;
- while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){
- clock_gettime(CLOCK_MONOTONIC, &ts);
- t1 = ts.tv_nsec;
- dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
- }
- // count fps.
- fps_count++;
- if (t1 < t0){
- printf("fps: %u\n", fps_count);
- fps_count = 0;
- }
- clock_gettime(CLOCK_MONOTONIC, &ts);
- t0 = ts.tv_nsec;
- }
-}
-</code></pre>
<p>
-時刻は<code>clock_gettime()</code>で測定して1秒未満の部分: <code>tv_nsec</code>だけを利用している。<code>tv_nsec</code>はナノ秒ナノで、10<sup>9</sup>を掛けている。<code>dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000</code>で前回の時刻と現在の時刻の少数部分を比較している。繰り上がりがあれば前回の時刻よりも現在の時刻の方が小さくなるので1秒足すことで調整している。</p>
+画面はTNだろうと思って買ったが、
+届いたら視野角が広くて非常に見易いので
+多分IPSであろう。
+これは結構うれしい。
+</p>
<p>
-FPSの計測の部分は、フレーム毎に<code>fps_count</code>を1ずつ増やし、ナノ秒が繰り上がった時点での<code>fps_count</code>を表示している。</p>
+全体的に綺麗な個体だ。
+</p>
<p>
-あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。</p>
-
-<h2>キーボード入力の処理</h2>
-<p>キーボードからの入力を受け取る:</p>
-<pre><code>XSelectInput(display, window,
- ExposureMask|KeyPressMask|KeyReleaseMask);
-</code></pre>
-<p>ここではキーボードのキーを押した時と離した時に<code>XEvent</code>の通知を受け取るように設定した。
+バッテリーは期待通り完全に消耗していて、
+起動後5秒ほどで切れてしまう。
</p>
<p>
-<code>XNextEvent()</code>からひとつずつ入力を受け取ると、複数のキーが同時に押された時にうまく処理できなかったので、押されているキーを配列に保存しておくことにした:</p>
-<pre><code>enum Keys {
- Key_D,
- Key_S,
- Key_A,
- Key_W,
- Key_Space,
- Num_Key, //number of keys in this enum
-};
-enum Key_State {
- Key_Up,
- Key_Down,
-};
-
-int key_state[Num_Key];
-</code></pre>
-
+あとwindows用の日本語キーボードはあんまり
+好きではない。
+いちばん下の段がぎゅうぎゅう詰めでスペースが
+小さすぎる。
+</p>
<p>
-入力の処理は<code>handle_inputs()</code>関数内で行なう。<code>A</code>、<code>S</code>、<code>D</code>、<code>W</code>のうちどれかのキーが押されているとそれぞれ左、下、右、上方向に速度を加算するようにした。また、<code>Q</code>が押されるか、windowが破壊されると<code>quit</code>フラグを<code>1</code>にしてメインループから抜けるようにしている:</p>
-<pre><code>int quit;
-
-void
-handle_inputs(void)
-{
- XEvent event;
- while (XPending(display) > 0) {
- XNextEvent(display, &event);
- switch (event.type) {
- case KeyPress: {
- switch (XLookupKeysym(&event.xkey, 0)) {
- case 'q':
- quit = 1;
- break;
- case 'd':
- key_state[Key_D] = Key_Down;
- break;
- case 'a':
- key_state[Key_A] = Key_Down;
- break;
- case 'w':
- key_state[Key_W] = Key_Down;
- break;
- case 's':
- key_state[Key_S] = Key_Down;
- break;
- default:
- break;
- }
- } break;
- case KeyRelease: {
- switch (XLookupKeysym(&event.xkey, 0)) {
- case 'd':
- key_state[Key_D] = Key_Up;
- break;
- case 'a':
- key_state[Key_A] = Key_Up;
- break;
- case 'w':
- key_state[Key_W] = Key_Up;
- break;
+バッテリーを交換するのが先かキーボードをUS配列にするのが先か
+悩ましいところだ。
+</p>
+]]></description>
+</item>
+<item>
+<title>Xlibで遊んでみる6</title>
+<link>https://www.mtkn.jp/computer/xlib_playground6.html</link>
+<guid>https://www.mtkn.jp/computer/xlib_playground6.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Xlibで遊んでみる6</h1>
+<time>2023-01-25</time>
- case 's':
- key_state[Key_S] = Key_Up;
- break;
- default:
- break;
- }
- } break;
- case ClientMessage: {
- if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
- quit = 1;
- }
- } break;
- default:
- break;
- }
- }
+<p>
+前回: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
+</p>
+<p>
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+</p>
- vx = vy = 0;
- if (key_state[Key_D] == Key_Down)
- vx += 300;
- if (key_state[Key_A] == Key_Down)
- vx += -300;
- if (key_state[Key_S] == Key_Down)
- vy += 300;
- if (key_state[Key_W] == Key_Down)
- vy += -300;
-}
+<h2>ワールドマップの作成</h2>
+<p>
+ゲームのワールドマップを作製した。ここでは文字列として登録した。なにもないところは「<code>.</code>」、ブロックの場所は「<code>b</code>」、プレーヤーは「<code>p</code>」とした:
+</p>
+<pre><code>char worldmap[WORLD_WIDTH * WORLD_HEIGHT + 1] =
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"........b......................................................................."
+"................................................................................"
+"................................................................................"
+"....b..........................................................................."
+"................................................................................"
+"................b..............................................................."
+"..........................................................b..........b.........."
+"................................................................................"
+".......................b........................................................"
+"...........................................b...................................."
+"...........................................b...................................."
+"................................................................................"
+"..................b............................................................."
+"................................................................................"
+"...........................................b...................................."
+"................................................................................"
+"................................................................................"
+"...........................b...................................................."
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"....................................bbbbbbbbbb.................................."
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"..............................................bbbbbbbbbb........................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"....................................bbbbbbbbbb.................................."
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"..........................bbbbbbbbbb............................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................................................................................"
+"................bbbbbbbbbb......................................................"
+"................................................................................"
+"................................................................................"
+"...p............................................................................"
+"bbbbbbbbbbbbbbbbbbbbbbbbb.......bbbbbbbbbbbbbbbbbbbbbbbb...bbbbbbbbbbbbbbbbbbbbb"
+"........................b.......b......................b...b...................."
+"........................b.......b......................b...b...................."
+"........................b.......b......................b...b...................."
+"........................b.......b......................b...b....................";
</code></pre>
-<p>
-入力によって変更された速度は、<code>main()</code>関数内で次の座標を計算するために使用される:
-</p>
-<pre><code>float px = 200, py = 200;
-float vx = 0, vy = 0;
-int width = 40, height = 40;
+<h2>プレイヤーの作成</h2>
+<p>プレイヤーには重力をかけたいので、まずは四角形に加速度を追加:</p>
+<pre><code>struct rect {
+ float ppx, ppy;
+ float px, py;
+ float vx, vy;
+ float ax, ay; // acceleration
+ int w, h;
+ int m;
+};
+</code></pre>
+<p>ワールドマップを読み込み、その際にプレイヤーに重力を付加:</p>
+<pre><code>struct rect block[NUM_RECT];
+struct rect player;
-int
-main(void)
-{
- /* ... */
- quit = 0;
- while (!quit) {
- handle_input()
- /* ... */
- px = px + vx * dt / 1000 / 1000 / 1000;
- py = py + vy * dt / 1000 / 1000 / 1000;
- // bind within the window
- if (px < 0)
- px = 0;
- if (win_width < px + width)
- px = win_width - width;
- if (py < 0)
- py = 0;
- if (win_height < py + height)
- py = win_height - height;
+/* ... */
- XClearArea(display, window,
- 0, 0, // position
- win_width, win_height, // width and height
- False);
- XFillRectangle(display, window, gc,
- px, py, // position
- width, height); // width and height
+ int bi = 0;
+ for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
+ if (world_map[i] == 'b') {
+ block[bi].ppx = block[bi].px = i % WORLD_WIDTH * BLOCK_SIZE;
+ block[bi].ppy = block[bi].py = i / WORLD_WIDTH * BLOCK_SIZE;
+ block[bi].ax = 0;
+ block[bi].ay = 0;
+ block[bi].vx = 0;
+ block[bi].vy = 0;
+ block[bi].w = block[bi].h = BLOCK_SIZE;
+ block[bi].m = block[bi].w * block[bi].h;
+ bi++;
+ } else if (world_map[i] == 'p') {
+ player.ppx = player.px = i % WORLD_WIDTH * BLOCK_SIZE;
+ player.ppy = player.py = i / WORLD_WIDTH * BLOCK_SIZE;
+ player.vx = 0;
+ player.vy = 0;
+ player.ax = 0;
+ player.ay = GRAVITY;
+ player.w = player.h = BLOCK_SIZE;
+ player.m = player.w * player.h;
+ }
}
- /* ... */
-}
</code></pre>
-<h2>完成品</h2>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex2/ex2.c.html">ソースコード</a>
-<p>色を変えてみた。</p>
-<video controls>
- <source src="videos/ex2.webm" type="video/webm">
-</video>
-
-<h2>参考</h2>
-<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-</ul>
-<p>次の記事: <a href="xlib_playground3.html">Xlibで遊んでみる3</a>
-</p>
-]]></description>
-</item>
-<item>
-<title>Xlibで遊んでみる1</title>
-<link>https://www.mtkn.jp/computer/xlib_playground1.html</link>
-<guid>https://www.mtkn.jp/computer/xlib_playground1.html</guid>
-<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる1</h1>
-<time>2022-12-21</time>
-
-<h2>はじめに</h2>
-<p>X11でGUIのプログラミングをしてみようと思い、してみた。X11用の低レベルのライブラリはXlibとxcbの二つがあるようだ。x.orgのウェブページを見てみると、Xlibは古く、xcbに置きかわりつつあるという。そのため、新しくなにかを作る場合はxcbを使うようにとのことである。ところがこのxcbはドキュメンテーションに乏しく、X11を触るのが初めての人間にはなにをどうすればいいのかほとんど分からなかった。知らない関数や構造体やらがでてきても(殆ど全部知らないものだが)、その関数なり構造体なりの説明がどこにも見当たらない。manページもない。あるのはdoxygenなるものでソースコードのコメントから自動生成したいい加減なものだけで、使いものにならない。</p>
-<p>とりあえずX11のことを少しは理解してからでないと初められそうもないと思い、もう少しましな情報があるXlibから始めることにした。</p>
-<p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。
+<p>ユーザーからの入力を受けとり、プレイーヤの加速度等を変更。<code>A</code>、<code>D</code>でそれぞれ左右に加速し、地面に接しているときに<code>space</code>キーでジャンプさせる:
</p>
-
-<h2>初期設定</h2>
-<p>ディスプレイを開き、ウィンドウを作成する。変数はとりあえずグローバルに宣言することにした。<code>main</code>関数はできるだけ小さくして実際の処理はそれぞれの関数にさせてみる:</p>
-<pre><code>
-#include <stdio.h>
-#include <stdlib.h>
-#include <X11/Xlib.h>
-
-/* macros */
-#define INIT_WIDTH 800
-#define INIT_HEIGHT 600
-
-/* variables */
-Display *display;
-Window window;
-unsigned int win_width = INIT_WIDTH, win_height = INIT_HEIGHT;
-GC gc;
-Atom wm_delete_window;
-
-void
-setup(void)
+<pre><code>void
+handle_inputs(int key_state[])
{
- // Open a display.
- if ((display = XOpenDisplay(NULL)) == NULL){
- fprintf(stderr, "ERROR: could not open display\n");
- exit(1);
+ if (key_state[KEY_Q] == KEY_DOWN){
+ next_menu = GAME_OVER;
+ return;
+ }
+ if (key_state[KEY_D] == KEY_DOWN) {
+ if (player.vx > 0) {
+ player.ax = 500;
+ } else {
+ player.ax = 1000;
+ }
+ } else if (key_state[KEY_A] == KEY_DOWN) {
+ if (player.vx > 0) {
+ player.ax = -1000;
+ } else {
+ player.ax = -500;
+ }
+ } else {
+ if (player_is_falling)
+ player.ax = -player.vx;
+ else
+ player.ax = -3 * player.vx;
}
- // Create a window.
- window = XCreateSimpleWindow(
- display,
- XDefaultRootWindow(display),
- 0, 0,
- win_width, win_height,
- 0, 0, // border properties
- 0); // background color: black
- XStoreName(display, window, "UNKO");
-
- // Setup a graphical context.
- gc = XCreateGC(display, window, 0, NULL);
- XSetForeground(display, gc, 0x0000FF);
-
- // Kill the application when the window is destroyed.
- wm_delete_window = XInternAtom(display,
- "WM_DELETE_WINDOW", False);
- XSetWMProtocols(display, window, &wm_delete_window, 1);
-
- // Setup which input to process.
- XSelectInput(display, window,
- ExposureMask|KeyPressMask|KeyReleaseMask);
-
- // Actually draw the window.
- XMapWindow(display, window);
-}
-void
-clean_up(void)
-{
- XCloseDisplay(display);
+ if (player.vx < -200) player.vx = -200;
+ if (player.vx > 200) player.vx = 200;
+ if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN)
+ player.vy = -450;
}
</code></pre>
-<p>適当な四角形のものを表示し、その位置を時間の関数として動かしてみる。</p>
-<pre><code>#include <time.h>
-#include <math.h>
-
-int
-main(void)
+<p>変更した加速度は<code>rect_next_tick()</code>関数で次の位置を計算するのに使用。また画面の下に落ちた時にゲームオーバーになるように設定:</p>
+<pre><code>void
+rect_next_tick(struct rect *s, long ndt) // nano second
{
- int px, py;
- int quit;
- struct timespec ts;
- XEvent event;
-
- setup();
- quit = 0;
-
- while (!quit){
- while(XPending(display) > 0){
- XNextEvent(display, &event);
- switch (event.type){
- case KeyPress: {
- switch (XLookupKeysym(&event.xkey, 0)){
- case 'q':
- quit = 1;
- break;
- default:
- break;
- }
- } break;
- case ClientMessage: {
- if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
- quit = 1;
- }
- } break;
- default:
- break;
- }
- }
- clock_gettime(CLOCK_MONOTONIC, &ts);
- px = 200 + (int) (100 * sinf(ts.tv_sec + ts.tv_nsec / 1000.0 / 1000 / 1000));
- py = 200 + (int) (100 * cosf(ts.tv_sec + ts.tv_nsec / 1000.0 / 1000 / 1000));
- XClearArea(display, window,
- 0, 0, // position
- win_width, win_height, // width and height
- False);
- XFillRectangle(display, window, gc,
- px, py, // position
- 100, 100); // width and height
+ s->ppx = s->px;
+ s->ppy = s->py;
+ s->vx += s->ax * ndt / 1000 / 1000 / 1000;
+ s->vy += s->ay * ndt / 1000 / 1000 / 1000;
+ s->px += s->vx * ndt / 1000 / 1000 / 1000;
+ s->py += s->vy * ndt / 1000 / 1000 / 1000;
- ts.tv_sec = 0;
- ts.tv_nsec = 10 * 1000 * 1000;
- nanosleep(&ts, NULL);
+ // bind within the window
+ if (s->px < 0) {
+ s->px = 0;
+ //s->vx *= -1;
+ }
+ if (win_width < s->px + s->w) {
+ s->px = win_width - s->w;
+ //s->vx *= -1;
}
-
- cleanup();
- return 0;
+ // game over when fall out of the screen
+ if (s->py > win_height)
+ next_menu = GAME_OVER;
}
</code></pre>
-<p>ここまでのコードはgitリポジトリの<a href="https://git.mtkn.jp/xlib_playground/file/ex1/ex1.c.html">ex1/ex1.c</a>にある。</p>
-<h2>完成品:</h2>
+
+<h2>完成品</h2>
+<p>
+<a href="https://git.mtkn.jp/xlib_playground/file/ex6/ex6.c.html">git</a>
+</p>
+<p>
<video controls>
- <source src="videos/ex1.webm" type="video/webm">
+<source src="videos/ex6.webm" type="video/webm">
</video>
+</p>
<h2>参考</h2>
<ul>
<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-<li><a href="https://www.youtube.com/watch?v=764fnfEb1_c">X11 App in C with Xlib(youtube video by tsoding)</a></li>
</ul>
-
-<a href="xlib_playground2.html">次の記事</a>
]]></description>
</item>
<item>
-<title>Arch Linuxのインストール</title>
-<link>https://www.mtkn.jp/computer/archlinux_installation.html</link>
-<guid>https://www.mtkn.jp/computer/archlinux_installation.html</guid>
+<title>Xlibで遊んでみる5</title>
+<link>https://www.mtkn.jp/computer/xlib_playground5.html</link>
+<guid>https://www.mtkn.jp/computer/xlib_playground5.html</guid>
<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Arch Linuxのインストール</h1>
-<time>2021-03-25</time>
-
-<h2>ハードウェア構成</h2>
-
-<h2>インストールの準備</h2>
-
-<h3>インストールメディアの入手</h3>
-
-<h3>署名の検証</h3>
-<pre><code>$ gpg --keyserver-options auto-key-retrieve --verify archlinux-<i>version</i>-x86_64.iso.sig
-</code></pre>
-
-<h3>インストールメディアの準備</h3>
-<pre><code>$ sudo dd bs=4M if=<i>path/to/arch/linux/iso</i> of=/dev/sd<i>X</i> status=progress oflag=sync
-</code></pre>
-
-<h3>ライブ環境の起動</h3>
-Arch Linux install medium (x86_64, UEFI)を選択
-
-<h3>インストールの記録</h3>
-<pre><code># script install.log
-</code></pre>
-
-<h3>起動モードの確認</h3>
-<pre><code># ls /sys/firmware/efi/efivars
-</code></pre>
-エラーが出なければUEFI。
+<description><![CDATA[<h1>Xlibで遊んでみる5</h1>
+<time>2023-01-03</time>
-<h3>インターネットへ接続</h3>
-<p>
-ネットワークインターフェイスが認識されているか確認:
-</p>
-<pre><code># ip link
-</code></pre>
-<p>
-Wi-Fi接続:
-</p>
-<pre><code># iwctl
-[iwd]# device list
-...
-[iwd]# exit
-</code></pre>
<p>
-接続を確認:
+前回: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
</p>
-<pre><code># ping archlinux.jp
-</code></pre>
-
-<h3>システムクロックの更新</h3>
-<pre><code># timedatectl set-ntp true
-</code></pre>
-
-<h3>パーティショニング</h3>
-<pre><code>sd<i>X</i>
-├sd<i>X</i>1 512M EFI System /boot
-└sd<i>X</i>2 lest Linux filesystem /
-</code></pre>
-<pre><code># lsblk
-# fdisk /dev/sd<i>X</i>
-Command (m for help): d
-...
-Command (m for help): w
-</code></pre>
-
-<h3>パーティションのフォーマット</h3>
-<pre><code># mkfs.fat -F32 /dev/sd<i>X</i>1
-# mkfs.ext4 /dev/sd<i>X</i>2
-</code></pre>
-
-<h3>ファイルシステムのマウント</h3>
-<pre><code># mount /dev/sd<i>X</i>2 /mnt
-# mkdir /mnt/boot
-# mount /dev/sd<i>X</i>1 /mnt/boot
-</code></pre>
-
-<h2>インストール</h2>
-<h3>ミラーの選択</h3>
-日本のサーバーを上に持ってくる:
-<pre><code># vim /etc/pacman.d/mirrorlist
-</code></pre>
-
-<h3>必須パッケージのインストール</h3>
-<pre><code># pacstrap /mnt base base-devel linux linux-firmware man-db man-pages
-</code></pre>
-
-<h3>fstabの生成</h3>
-<pre><code># genfstab -U /mnt >> /mnt/etc/fstab
-</code></pre>
-
-<h3>chroot</h3>
-<pre><code># arch-chroot /mnt
-</code></pre>
-
-<h3>text editorをインストール</h3>
-<pre><code># pacman -S neovim
-</code></pre>
-
-<h3>タイムゾーン</h3>
-<pre><code># ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
-# hwclock --systohc
-</code></pre>
-
-<h3>ローカリゼーション</h3>
-<code>en_US.UTF-8</code>と<code>ja_JP.UTF-8</code>をコメントイン:
-<pre><code># nvim /etc/locale.gen
-</code></pre>
-ロケールを生成:
-<pre><code># locale-gen
-</code></pre>
-<pre><code># nvim /etc/locale.conf
-LANG=en_US.UTF-8
-</code></pre>
-
-<h3>ネットワーク設定</h3>
<p>
-無線接続用のソフトをインストール
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
</p>
-<pre><code># pacman -S networkmanager
-</code></pre>
+<h2>円の衝突判定とその処理</h2>
<p>
-ホストネームの設定
+前回四角形で行っていた衝突判定とその処理を今回は円でした。衝突の判定は二つの円の中心間の距離と、各円の半径の和を比較するだけなので簡単である:
</p>
-<pre><code># nvim /etc/hostname
-<i>myhostname</i>
-</code></pre>
-<pre><code># nvim /etc/hosts
-127.0.0.1 localhost
-::1 localhost
-127.0.1.1 <i>myhostname</i>.localdomain <i>myhostname</i>
-</code></pre>
+<pre><code>struct circle {
+ float ppx, ppy; // previous position (center)
+ float px, py; // current position (center)
+ float vx, vy; // velocity
+ int r; // radius
+ int m; // mass
+};
-<h3>Rootパスワード</h3>
-<pre><code># passwd
+int
+circle_test_collision(struct circle *c1, struct circle *c2)
+{
+ return (c1->px - c2->px) * (c1->px - c2->px) +
+ (c1->py - c2->py) * (c1->py - c2->py) <
+ (c1->r + c2->r) * (c1->r + c2->r);
+}
</code></pre>
-<h3>ブートローダー</h3>
-<p>
-インストール
-</p>
-<pre><code># pacman -S grub efibootmgr
-# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=boot
-</code></pre>
<p>
-メイン設定ファイルの生成
+衝突後は前回と同じく弾性衝突として処理した。四角形とは違い、衝突方向の場合分けが不要なので楽である。
</p>
-<pre><code># grub-mkconfig -o /boot/grub/grub.cfg
-</code></pre>
+<pre><code>
+void
+circle_handle_collision_mm(struct circle *c1, struct circle *c2)
+{
+ if (!circle_test_collision(c1, c2))
+ return;
-<h3>マイクロコードのアップデートを有効化</h3>
-<pre><code># pacman -S intel-ucode
-# grub-mkconfig -o /boot/grub/grub.cfg
-上のん無駄やんけ
-</code></pre>
+ float col_px = c2->px - c1->px;
+ float col_py = c2->py - c1->py;
+ float col_pr = sqrtf(col_px * col_px + col_py * col_py);
+ col_px /= col_pr;
+ col_py /= col_pr;
-<h2>再起動</h2>
-<pre><code># exit #chrootを抜ける
-</code></pre>
+ c1->px = c1->px - col_px / 2;
+ c1->py = c1->py - col_py / 2;
+ c2->px = c2->px + col_px / 2;
+ c2->py = c2->py + col_py / 2;
+}
-<h3>インストールの記録を保存</h3>
-<pre><code># exit #scriptを停止
-# mv install.log /mnt/root
-</code></pre>
+void
+circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
+{
+ if(!circle_test_collision(c1, c2))
+ return;
-<h3>アンマウント</h3>
-<pre><code># umount -R /mnt
-</code></pre>
+ float col_px = c2->px - c1->px;
+ float col_py = c2->py - c1->py;
+ float col_pr = sqrtf(col_px * col_px + col_py * col_py);
+ col_px /= col_pr;
+ col_py /= col_pr;
+ float nor_px = col_py;
+ float nor_py = -col_px;
-<h3>再起動</h3>
-<pre><code># reboot
-</code></pre>
+ float m1 = c1->m;
+ float m2 = c2->m;
-<h2>インストール後</h2>
-<h3>システム管理</h3>
-<p>
-一般ユーザーの作成
-</p>
-<pre><code># useradd -m -G wheel -s /bin/bash kenji
-# passwd kenji
-</code></pre>
-<p>
-作成したユーザーをsudoerに追加
-</p>
-<pre><code># visudo
-%wheel ALL=(ALL) ALL #uncomment
-</code></pre>
-<p>
-一般ユーザーとしてログインしなおす。
-</p>
+ float col_1v = c1->vx * col_px + c1->vy * col_py;
+ float col_2v = c2->vx * col_px + c2->vy * col_py;
+
+ float col_1vxn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_px;
+ float col_1vyn = (2*m2/(m1+m2)*col_2v + (m1-m2)/(m1+m2)*col_1v) * col_py;
+ float col_2vxn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_px;
+ float col_2vyn = (2*m1/(m1+m2)*col_1v + (m2-m1)/(m1+m2)*col_2v) * col_py;
-<h3>パッケージ管理</h3>
-<p>
-AUR
-</p>
+ float nor_1vx = nor_px * (c1->vx * nor_px + c1->vy * nor_py);
+ float nor_1vy = nor_py * (c1->vx * nor_px + c1->vy * nor_py);
+ float nor_2vx = nor_px * (c2->vx * nor_px + c2->vy * nor_py);
+ float nor_2vy = nor_py * (c2->vx * nor_px + c2->vy * nor_py);
+ c1->vx = col_1vxn + nor_1vx;
+ c1->vy = col_1vyn + nor_1vy;
+ c2->vx = col_2vxn + nor_2vx;
+ c2->vy = col_2vyn + nor_2vy;
+ circle_handle_collision_mm(c1, c2);
+}
+</code></pre>
-<h3>dotfilesを同期</h3>
+<h2>完成品</h2>
<p>
-gitのインストール
+<a href="https://git.mtkn.jp/xlib_playground/file/ex5/ex5.c.html">git</a>
</p>
-<pre><code>$ sudo pacman -S git
-$ git config --global user.name "<i>First-name</i> <i>Family-name</i>"
-$ git config --global user.email "<i>username</i>@<i>example.com</i>"
-</code></pre>
<p>
-dotfilesを同期
+<video controls>
+<source src="videos/ex5.webm" type="video/webm">
+</video>
</p>
-<pre><code>$ mkdir ~/.local
-$ cd ~/.local
-$ git clone https://github.com/<i>dotfilesのリポジトリ</i>
-$ ln -sf ~/.local/dotfiles/.bash* ~/
-$ mkdir .config
-$ ln -s ~/.local/dotfiles/.config/* ~/.config/
-...
-</code></pre>
-<h3>GUI</h3>
-<p>
-グラフィックドライバのインストール
-</p>
-<pre><code>$ sudo pacman -S nvidia nvidia-utils xorg-xinit
-</code></pre>
+<h2>参考</h2>
+<ul>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+</ul>
<p>
-window managerをインストール
+次の記事: <a href="xlib_playground6.html">Xlibで遊んでみる6</a>
</p>
-<pre><code>$ sudo pacman -S i3-wm i3blocks dmenu
-</code></pre>
-ドライバをインストールしたらxorg-server等も依存関係として入った。
+]]></description>
+</item>
+<item>
+<title>Xlibで遊んでみる4</title>
+<link>https://www.mtkn.jp/computer/xlib_playground4.html</link>
+<guid>https://www.mtkn.jp/computer/xlib_playground4.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Xlibで遊んでみる4</h1>
+<time>2023-01-02</time>
+
<p>
-フォントをインストール
+前回: <a href="xlib_playground3.html">Xlibで遊んでみる3</a>
</p>
-<pre><code>$ sudo pacman -S noto-fonts-cjk noto-fonts-emoji ttf-joypixels ttf-font-awesome ttf-liberation
-</code></pre>
-
<p>
-ターミナル(st)をインストール
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
</p>
-<pre><code>$ mkdir ~/.local/src
-$ cd ~/.local/src
-$ git clone git://git.suckless.org/st
-$ cd st
-$ sudo make install
-</code></pre>
+<h2>衝突判定とその処理</h2>
<p>
-再起動
-</p>
-<pre><code># sudo reboot
-</code></pre>
-だめでした
+これまでは一つの四角形だけを描画していたが、今回は複数の四角形を作成して動かしてみた。ランダムな場所にランダムな運動量で動かして、他のものやウィンドウの縁とぶつかったら跳ね返るようにした。</p>
<p>
-ログイン時に次のエラー
+回転しない四角形どうしの衝突判定は簡単である。x軸方向とy軸方向の両方に重なりがあれば衝突している:
</p>
-<pre><code>xauth: error in locking authority file /home/kenji/.cache/X11/Xauthority
+<pre><code>struct square {
+ float ppx, ppy; // previous position
+ float px, py; // current position
+ float vx, vy; // velocity
+ int w, h; // width and height
+};
+
+int
+test_collision(struct square *s1, struct square* s2)
+{
+ return s1->px < s2->px + s2->w && s2->px < s1->px + s1->w &&
+ s2->py < s1->py + s1->h && s1->py < s2->py + s2->h;
+}
</code></pre>
+
<p>
-<code>~/.cache/X11</code>というディレクトリがないのが原因のようだ。
-<code>.bash_profile</code>でXauthorityの場所を変更していたのにディレクトリを作っていなかった。
+衝突後の処理は多少めんどくさかった。衝突した時は既にめりこんでいるので、まずはそれぞれをめりこんだ距離の半分ずつずらして衝突を解消するようにした。この際、x軸方向にぶつかったのか、y軸方向にぶつかったのかで、それぞれの軸方向にひっぺがすようにしている。二つの四角形の各軸に関するめりこんだ距離<code>lapx</code>、<code>lapy</code>と各軸に関する相対速度<code>rel_vx</code>、<code>rel_vy</code>の比を比べればどちらの軸方向にぶつかったかが分かるはずである、多分 :
</p>
-<pre><code>$ mkdir ~/.cache/X11
+<pre><code>void
+handle_collision_mm(struct square *s1, struct square *s2)
+{
+ if (!test_collision(s1, s2))
+ return;
+
+ float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px);
+ float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py);
+ float rel_vx = max(s1->vx - s2->vx, s2->vx - s1->vx);
+ float rel_vy = max(s1->vy - s2->vy, s2->vy - s1->vy);
+
+ if (lapx / rel_vx < lapy / rel_vy) {
+ if (s1->px + s1->w < s2->px + s2->w / 2) {
+ s1->px -= lapx / 2;
+ s2->px += lapx / 2;
+ } else {
+ s1->px += lapx / 2;
+ s2->px -= lapx / 2;
+ }
+ } else {
+ if (s1->py + s1->h < s2->py + s2->h / 2) {
+ s1->py -= lapy / 2;
+ s2->py += lapy / 2;
+ } else {
+ s1->py += lapy / 2;
+ s2->py -= lapy / 2;
+ }
+ }
+}
</code></pre>
<p>
-として解決。
+衝突は弾性衝突として、衝突したそれぞれの四角形の速度を更新した。質量は四角形の面積として計算している。衝突後の速度はエネルギー保存則と運動量保存則から導いたのでしんどかった。
</p>
+<pre><code>void
+handle_collision_elastic(struct square *s1, struct square *s2)
+{
+ if(!test_collision(s1, s2))
+ return;
-<h3>日本語入力</h3>
-<p>
-ibusとibus-skkをインストール
-</p>
-<pre><code>$ sudo pacman -S ibus ibu-skk skk-jisyo
-</code></pre>
-<pre><code>vim ~/.config/X11/xinitrc
-export DefaultIMModule=ibus
-export GTK_IM_MODULE=ibus
-export QT_IM_MODULE=ibus
-export XMODIFIERS="@im=ibus"
+ float v1, v2;
+ float m1 = s1->w * s1->h;
+ float m2 = s2->w * s2->h;
-ibus-daemon --xim &
-</code></pre>
+ float lapx = min(s1->px + s1->w, s2->px + s2->w) - max(s1->px, s2->px);
+ float lapy = min(s1->py + s1->h, s2->py + s2->h) - max(s1->py, s2->py);
-<h3>ブラウザ(brave)をインストール</h3>
-<pre><code>$ cd ~/.local/src
-$ git clone https://aur.archlinux.org/brave-bin.git
-$ cd brave-bin
-$ makepkg -si
-</code></pre>
+ if (lapx < lapy) {
+ v1 = s1->vx;
+ v2 = s2->vx;
+ s1->vx = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1;
+ s2->vx = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2;
+ } else {
+ v1 = s1->vy;
+ v2 = s2->vy;
+ s1->vy = 2*m2/(m1+m2)*v2 + (m1-m2)/(m1+m2)*v1;
+ s2->vy = 2*m1/(m1+m2)*v1 + (m2-m1)/(m1+m2)*v2;
+ }
-<h3>音</h3>
-<pre><code>$ sudo pacman -S alsa-utilst pulseaudio
-$ pulseaudio --start
+ handle_collision_mm(s1, s2);
+}
</code></pre>
-
+<h2>サブティック</h2>
<p>
-ssh
+この名前が適切かどうか分からないが、前のフレームから次のフレームまでの時間をさらに何等分かして衝突判定の制度を上げた(マクロは括弧でかこって分かりにくいバグを防げとどこかに書いていたのでそうすることにした):
</p>
-<pre><code>$ sudo pacman -S openssh
-$ mkdir ~/.ssh
-$ cd ~/.ssh
-$ ssh-keygen -t rsa
+<pre><code>#define SUB_TIC (4)
+
+void
+game_play(void)
+{
+ /* ... */
+ while (next_menu == GAME_PLAY) {
+ /* ... */
+ for (int j = 0; j < SUB_TICK; j++) {
+ for (int i = 0; i < NUM_SQUARE; i++)
+ next_tick(&square[i], 1000 * 1000 * 1000 / FPS / SUB_TICK);
+
+ for (int i = 0; i < NUM_SQUARE; i++)
+ for (int j = i + 1; j < NUM_SQUARE; j++) {
+ handle_collision_elastic(&square[i], &square[j]);
+ /* ... */
+ }
+ /* ... */
+ }
+ /* ... */
+ }
+ /* ... */
+}
</code></pre>
-]]></description>
-</item>
-<item>
-<title>RP2040 SDKなしでLチカ</title>
-<link>https://www.mtkn.jp/computer/rp2040_1.html</link>
-<guid>https://www.mtkn.jp/computer/rp2040_1.html</guid>
-<pubDate>Tue, 9 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>RP2040 SDKなしでLチカ</h1>
-<time>2023-04-25</time>
-<h2>はじめに</h2>
-<p>
-パタヘネのRISC-V<sup>[1]</sup>版を買って一通り読んだらアセンブリ言語で組込のプログラミングがしたくなった。RISC-Vのマイコンボードが欲しかったのだが、安くていい感じのものが見付からなかった。代わりに秋月電子通商でArmのものがあった。RP2040マイコンボードキット<sup>[2]</sup>というものである。ウェブ上の情報も多く、データシート<sup>[3]</sup>もしっかりしていそうなので、とりあえずこれを買ってみた。</p>
-<p>
-一般的にはSDK<sup>[4]</sup>をダウンロードしてあらかじめ用意されたライブラリを使って開発するようだが、これはビルドシステムとしてcmakeというのを使っている。これがOpenBSDでは何かエラーがでて動かなかった。僕はこういう便利ツールが嫌いだ。どうせ使わんからいいんやけど。関係ないけど途中から開発環境がLinuxに替わった。SDKには便利な関数がたくさん用意されているので楽である。ハードウェアの面倒な部分がプログラマから見えないようにしているからである。しかし今回はその面倒な部分に触れてみたくて買ったので、SDKを使うと意味がない。</p>
+<h2>完成品</h2>
<p>
-ということでSDKなしで開発してみる。とりあえず定番のLチカをば。</p>
+<a href="https://git.mtkn.jp/xlib_playground/file/ex4/ex4.c.html">git</a>
+</p>
<p>
-ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>
+<video controls>
+<source src="videos/ex4.webm" type="video/webm">
+</video>
</p>
-<h2>動作環境</h2>
+<h2>参考</h2>
<ul>
-<li>Arch Linux 6.2.12-arch1-1
- <ul>
- <li>arm-none-eabi-binutils 2.40-1</li>
- <li>GNU Make 4.4.1</li>
- </ul>
-</li>
-<li>OpenBSD 7.3
- <ul>
- <li>arm-none-eabi-binutils 2.31.1</li>
- <li>make (バージョン?)</li>
- </ul>
-※<code>make flash</code>は動かん。<code>dmesg</code>でデバイス確認して手動でマウントする必要がある。
-</li>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
</ul>
+<p>
+次の記事: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
+</p>
+]]></description>
+</item>
+<item>
+<title>Xlibで遊んでみる3</title>
+<link>https://www.mtkn.jp/computer/xlib_playground3.html</link>
+<guid>https://www.mtkn.jp/computer/xlib_playground3.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Xlibで遊んでみる3</h1>
+<time>2023-01-02</time>
-<h2>Boot Process</h2>
<p>
-RP2040は電源を入れるといくつかの段階(ここでは関係ないので省略。データシート「2.8.1 Processor Controlled Boot Sequence」に詳しく書いてある)を踏んだあと、外部のフラッシュROMの先頭から256バイトを内部のSRAMにコピーして、フラッシュにプログラムが書き込まれているかどうか確認する。RP2040はフラッシュの先頭252バイトから計算したCRC32チェックサムを、直後の253バイト目から256バイトに記録することになっている。起動時にこのチェックサムを確認することで、フラッシュにプログラムが書き込まれているかどうか確かめている。コピーした最後の4バイトと起動時に最初の252バイトから計算したチェックサムが一致していれば、そのままコピーしてきた256バイトの先頭にPCをセットして実行を開始する。一致しなければUSBデバイスモードに切り替わり、パソコンに接続するとストレージとして認識される。このストレージにUF2という形式に変換したプログラムをコピーするとプログラムがフラッシュROMやSRAMに書き込まれる。
+前回: <a href="xlib_playground2.html">Xlibで遊んでみる2</a>
</p>
<p>
-以上のことから、プログラムを実行するためにはCRC32を計算し、UF2という形式に変換することが必要である。ソースコードからの流れは以下の通り:
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
</p>
-<pre>source bin bin with
-code ----------> object ------> elf --------> bin -------> with --------> crc32 in
- crc32 uf2 format
- assemble link objcopy bincrc bin2uf2
-</pre>
-<h2>CRC(巡回冗長検査)</h2>
-<p>
-入力のデータをごにょごにょしてある値を出力する。</p>
-<blockquote cite="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB">
-<p>
-データ転送等に伴う偶発的な誤りの検査によく使われている<sup>[5]</sup>。
-</p>
-</blockquote>
+<h2>画面サイズの変更</h2>
<p>
-らしい。
+画面サイズが変更された時に表示している四角形が画面の外側に出ないようにした。<code>XGetWindowAttributes()</code>で画面の情報を取得し、グローバル変数の<code>win_width</code>と<code>win_height</code>に幅と高さをそれぞれ代入して<code>next_tick()</code>で四角形の位置を画面に収まるようにしている:
</p>
+<pre><code>int win_width, win_height;
+
+void
+receive_events(int key_state[])
+{
+ XEvent event;
+ XWindowAttributes wattr;
+
+ while (XPending(display) > 0) {
+ XNextEvent(display, &event);
+ switch (event.type) {
+ case Expose: {
+ XGetWindowAttributes(display, window, &wattr);
+ win_width = wattr.width;
+ win_height = wattr.height;
+ } break;
+ /* ... */
+ }
+ }
+}
+
+void
+next_tick(long ndt) // nano second
+{
+ px = px + vx * ndt / 1000 / 1000 / 1000;
+ py = py + vy * ndt / 1000 / 1000 / 1000;
+ // bind within the window
+ if (px < 0)
+ px = 0;
+ if (win_width < px + width)
+ px = win_width - width;
+ if (py < 0)
+ py = 0;
+ if (win_height < py + height)
+ py = win_height - height;
+}
+</code></pre>
+
+<h2>メニュー画面の実装</h2>
<p>
-入力のビットを一列に並べて、除数で「割り算」していく。この「割り算」が多項式の除算に似ているので、この除数をCRC多項式というらしい。ただし多項式の除算と違い、引き算するところをXORする。CRC32の場合、除数は33ビットである。33ビットで割ると32ビットの余りが残る。この余りがCRC32のチェックサムである。除数は色々あるようだが、標準的なものがWikipedia<sup>[5]</sup>に列挙されている。除数<code>1011</code>を使ったCRC3の計算の手順は以下の通り:
+ゲームのようなものを作るうえでメニュー画面とその推移が必要である。ここではグローバル変数<code>next_menu</code>に現在のメニューを保存することにした。それぞれのメニューはそれぞれ関数として記述し、他のメニューに推移する必要が生じたときに<code>next_menu</code>を変更するようにした:
</p>
-<pre><code>1110101011011100110101101101111 入力(適当)
-1011 除数(4ビット)
--------------------------------
- 101101011011100110101101101111 結果(入力と除数のXOR)
- 1011
- ------------------------------
- 00001011011100110101101101111
- 1011
- -------------------------
- 000011100110101101101111
- 1011
- --------------------
- 1010110101101101111
- 1011
- -------------------
- 001110101101101111
- 1011
- ----------------
- 101101101101111
- 1011
- ---------------
- 00001101101111
- 1011
- ----------
- 110101111
- 1011
- ---------
- 11001111
- 1011
- --------
- 1111111
- 1011
- -------
- 100111
- 1011
- ------
- 01011
- 1011
- ----
- 000 CRC3チェックサム
+<pre><code>enum next_menu {
+ START_MENU,
+ GAME_PLAY,
+ GAME_OVER,
+ QUIT,
+};
+
+int next_menu = START_MENU;
+
+void
+start_menu(void)
+{
+ XEvent event;
+ char *menu_char_q = "press q to quit.";
+ char *menu_char_s = "press <space> to start.";
+
+ XClearArea(display, window,
+ 0, 0, // position
+ win_width, win_height, // width and height
+ False);
+ XDrawString(display, window, gc,
+ win_width/2 - strlen(menu_char_q)/2, win_height/2,
+ menu_char_q, strlen(menu_char_q));
+ XDrawString(display, window, gc,
+ win_width/2 - strlen(menu_char_s)/2, win_height/2 + 20,
+ menu_char_s, strlen(menu_char_s));
+
+ while (next_menu == START_MENU) {
+ XNextEvent(display, &event);
+ switch (event.type) {
+ case Expose: {
+ XDrawString(display, window, gc,
+ win_width/2 - strlen(menu_char_q)/2,
+ win_height/2,
+ menu_char_q, strlen(menu_char_q));
+ XDrawString(display, window, gc,
+ win_width/2 - strlen(menu_char_s)/2,
+ win_height/2 + 20,
+ menu_char_s, strlen(menu_char_s));
+
+ } break;
+ case KeyPress: {
+ switch (XLookupKeysym(&event.xkey, 0)) {
+ case 'q':
+ next_menu = QUIT;
+ break;
+ case ' ':
+ next_menu = GAME_PLAY;
+ break;
+ default:
+ break;
+ }
+ } break;
+ case ClientMessage: {
+ if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
+ next_menu = QUIT;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
+int
+main(void)
+{
+ setup();
+ while (next_menu != QUIT){
+ switch (next_menu){
+ case START_MENU:
+ start_menu();
+ break;
+ case GAME_PLAY:
+ game_play();
+ break;
+ case GAME_OVER:
+ game_over();
+ break;
+ default:
+ break;
+ }
+ }
+
+ cleanup();
+ return 0;
+}
</code></pre>
+<p><code>main()</code>関数がめっちゃすっきりした。</p>
+
+<h2>完成品</h2>
<p>
-普通の割り算と基本は同じであるが、引き算の部分だけXORになっている。</p>
-<p>
-以上の計算をプログラムの先頭252バイトに対して、33ビットの除数を用いて行う。データの並べ方は、上の例において左側を先頭に、フラッシュROM上の0番地から、各バイトは最上位ビットから順に並べる。入力のデータは253バイト目から256バイト目に<code>0</code>をひっつけて計算する。これは多分予め長さが分からないデータでも計算できるようにしたかったからかな。除数は<code>0x104c11db7</code>である(最上位ビットは常に1なのでデータシートでは省略されている)。</p>
-<p>
-入力データは1バイトづつ処理したいみたいである。多分通信等で使う都合である。この時XORは結合則が成り立つので1バイト処理した結果と次のバイトとをXORして次の処理の入力として利用することができる:
+<a href="https://git.mtkn.jp/xlib_playground/file/ex3/ex3.c.html">git</a>
</p>
-<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
-|......|
-111000110000000000000000000000000 先頭1バイト
-100000100110000010001110110110111 除数
-------------------------------------------------------------------------
-011000010110000010001110110110111
- 100000100110000010001110110110111
- -----------------------------------------------------------------------
- 010000001010000110010011011011001
- 100000100110000010001110110110111
- ----------------------------------------------------------------------
- 000000110010001110101000000000101
-|......|
- 110010001110101000000000101000000 1バイト目の結果
- |......|
- 10000001 入力の2バイト目
- ----------------------------------------------------------------
- 010010011110101000000000101000000 1バイト目の結果と2バイト目のXOR
- 100000100110000010001110110110111 除数
- ----------------------------------------------------------------
- 000100011011010010001111100110111
- .
- .
- .
-</code></pre>
<p>
-以上の操作は以下のようなアルゴリズムのループで実装できる。</p>
+<video controls>
+<source src="videos/ex3.webm" type="video/webm">
+</video>
+</p>
+
+<h2>参考</h2>
<ul>
-<li>前回の結果と、入力データの次のバイトをXOR</li>
-<li>
- <ul>
- <li>先頭の1ビットが1の場合、除数とXORを取り左シフト</li>
- <li>先頭の1ビットが0の場合、そのまま左シフト</li>
- </ul>
-</li>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
</ul>
<p>
-これを<code>for</code>ループで回す都合上、最初のバイトもXORを取る。上の例では最初は<code>0x0</code>とXORを取っているが、この値を<code>0x0</code>以外にすることもできる。そうした方がいろいろいいこともあるらしい。RP2040では<code>0xffffffff</code>を使う。更にこの工程を32ビットの<code>int</code>だけで行うことを考える:
+次の記事: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
+</p>
+]]></description>
+</item>
+<item>
+<title>Xlibで遊んでみる2</title>
+<link>https://www.mtkn.jp/computer/xlib_playground2.html</link>
+<guid>https://www.mtkn.jp/computer/xlib_playground2.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Xlibで遊んでみる2</h1>
+<time>2022-12-22</time>
+
+<p>前回: <a href="xlib_playground1.html">Xlibで遊んでみる1</a></p>
+<p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。
</p>
-<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
-11111111111111111111111111111111 0xffffffff
-11100011000000000000000000000000 先頭1バイトを24ビットシフト
--------------------------------- XOR
-00011100111111111111111111111111
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-00111001111111111111111111111110
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-01110011111111111111111111111100
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-11100111111111111111111111111000
-先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR:
-11001111111111111111111111110000 シフト
-00000100110000010001110110110111 除数の下位32ビット
--------------------------------- XOR
-11001011001111101110001001000111
-先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR:
-10010110011111011100010010001110 シフト
-00000100110000010001110110110111 除数の下位32ビット
--------------------------------- XOR
-10010010101111001101100100111001
-先頭1ビットが1なので1ビットシフトした後、除数の下位32ビットとXOR:
-00100101011110011011001001110010 シフト
-00000100110000010001110110110111 除数の下位32ビット
--------------------------------- XOR
-00100001101110001010111111000101
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-01000011011100010101111110001010
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-10000110111000101011111100010100 1バイト目の結果
+<h2>FPSの固定</h2>
+<p>前のフレームからの経過時間を計測して<code>1.0/FPS</code>を越えるまで待機させる。このときに<code>nanosleep()</code>を使うとなぜか上手くいかなかった。ナノ秒単位で処理できそうな名前なのに使えない。多分OSのコンテクストスイッチがどうとかいう話やと思う。知らんけど。組み込みとかで使うんかな?
+</p>
-10000001 入力の2バイト目
--------------------------------- XOR
-00000111111000101011111100010100
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-00001111110001010111111000101000
-.
-.
-.
-</code></pre>
<p>
-これを実装したのが以下のコード:</p>
-<pre><code>uint32_t
-crc32(uint8_t *idata, size_t len)
+とりあえず<code>while</code>ループの中でひたすら時刻を読んでいる。リソースの無駄遣いではないのだろうか:
+</p>
+<pre><code>#defin FPS 60
+
+int
+main(void)
{
- uint32_t pol = 0x04C11DB7;
- uint32_t c = 0xFFFFFFFF;
- uint32_t b;
+ long t0, t1, dt;
+ int fps_count;
- for (int i = 0; i < len; i++) {
- b = idata[i] << 24;
- c ^= b;
- for (int j = 0; j < 8; j++) {
- c = c >> 31 & 1 ? c << 1 ^ pol : c << 1;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t0 = ts.tv_nsec;
+
+ while (!quit) {
+ // fix fps
+ dt = 0;
+ while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t1 = ts.tv_nsec;
+ dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
+ }
+ // count fps.
+ fps_count++;
+ if (t1 < t0){
+ printf("fps: %u\n", fps_count);
+ fps_count = 0;
}
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t0 = ts.tv_nsec;
}
-
- return c;
}
</code></pre>
<p>
-<code>main()</code>関数では上の<code>crc32()</code>に、<code>idata</code>として入力となるバイナリデータの先頭を、<code>len</code>として<code>252</code>を渡してCRC32を計算させる。その後、出力先のファイルに入力元のデータをコピーしていき、253バイト目から256バイト目だけ、計算したCRC32に置き換える。入力元のこの場所にデータが書き込まれていないかどうかは確かめていない。
-</p>
-
-<h2>UF2(USB Flashing Format)</h2>
+時刻は<code>clock_gettime()</code>で測定して1秒未満の部分: <code>tv_nsec</code>だけを利用している。<code>tv_nsec</code>はナノ秒ナノで、10<sup>9</sup>を掛けている。<code>dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000</code>で前回の時刻と現在の時刻の少数部分を比較している。繰り上がりがあれば前回の時刻よりも現在の時刻の方が小さくなるので1秒足すことで調整している。</p>
<p>
-Microsoftが開発したフラッシュ書き込み用のファイル形式らしい:
-<blockquote cite="https://github.com/microsoft/uf2">
+FPSの計測の部分は、フレーム毎に<code>fps_count</code>を1ずつ増やし、ナノ秒が繰り上がった時点での<code>fps_count</code>を表示している。</p>
<p>
-UF2 is a file format, developed by Microsoft for PXT (also known as
-Microsoft MakeCode), that is particularly suitable for flashing microcontrollers
-over MSC (Mass Storage Class; aka removable flash drive)<sup>[6]</sup>.
+あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。</p>
+
+<h2>キーボード入力の処理</h2>
+<p>キーボードからの入力を受け取る:</p>
+<pre><code>XSelectInput(display, window,
+ ExposureMask|KeyPressMask|KeyReleaseMask);
+</code></pre>
+<p>ここではキーボードのキーを押した時と離した時に<code>XEvent</code>の通知を受け取るように設定した。
</p>
-</blockquote>
<p>
-このファイルに変換する上で必要な情報はGitHubのmicrosoft/uf2<sup>[6]</sup>に表として纏められている:
-<blockquote cite="https://github.com/microsoft/uf2">
-<table>
-<thead><tr>
-<th>Offset</th><th>Size</th><th>Value</th>
-</tr></thead>
-<tbody>
-<tr>
-<td>0</td>
-<td>4</td>
-<td>First magic number, <code>0x0A324655</code> (<code>"UF2\n"</code>)</td>
-</tr>
-<tr>
-<td>4</td>
-<td>4</td>
-<td>Second magic number, <code>0x9E5D5157</code></td>
-</tr>
-<tr>
-<td>8</td>
-<td>4</td>
-<td>Flags</td>
-</tr>
-<tr>
-<td>12</td>
-<td>4</td>
-<td>Address in flash where the data should be written</td>
-</tr>
-<tr>
-<td>16</td>
-<td>4</td>
-<td>Number of bytes used in data (often 256)</td>
-</tr>
-<tr>
-<td>20</td>
-<td>4</td>
-<td>Sequential block number; starts at 0</td>
-</tr>
-<tr>
-<td>24</td>
-<td>4</td>
-<td>Total number of blocks in file</td>
-</tr>
-<tr>
-<td>28</td>
-<td>4</td>
-<td>File size or board family ID or zero</td>
-</tr>
-<tr>
-<td>32</td>
-<td>476</td>
-<td>Data, padded with zeros</td>
-</tr>
-<tr>
-<td>508</td>
-<td>4</td>
-<td>Final magic number, <code>0x0AB16F30</code></td>
-</tr>
-</tbody>
-</table>
-</blockquote>
+<code>XNextEvent()</code>からひとつずつ入力を受け取ると、複数のキーが同時に押された時にうまく処理できなかったので、押されているキーを配列に保存しておくことにした:</p>
+<pre><code>enum Keys {
+ Key_D,
+ Key_S,
+ Key_A,
+ Key_W,
+ Key_Space,
+ Num_Key, //number of keys in this enum
+};
+enum Key_State {
+ Key_Up,
+ Key_Down,
+};
+
+int key_state[Num_Key];
+</code></pre>
+
+<p>
+入力の処理は<code>handle_inputs()</code>関数内で行なう。<code>A</code>、<code>S</code>、<code>D</code>、<code>W</code>のうちどれかのキーが押されているとそれぞれ左、下、右、上方向に速度を加算するようにした。また、<code>Q</code>が押されるか、windowが破壊されると<code>quit</code>フラグを<code>1</code>にしてメインループから抜けるようにしている:</p>
+<pre><code>int quit;
+
+void
+handle_inputs(void)
+{
+ XEvent event;
+ while (XPending(display) > 0) {
+ XNextEvent(display, &event);
+ switch (event.type) {
+ case KeyPress: {
+ switch (XLookupKeysym(&event.xkey, 0)) {
+ case 'q':
+ quit = 1;
+ break;
+ case 'd':
+ key_state[Key_D] = Key_Down;
+ break;
+ case 'a':
+ key_state[Key_A] = Key_Down;
+ break;
+ case 'w':
+ key_state[Key_W] = Key_Down;
+ break;
+ case 's':
+ key_state[Key_S] = Key_Down;
+ break;
+ default:
+ break;
+ }
+ } break;
+ case KeyRelease: {
+ switch (XLookupKeysym(&event.xkey, 0)) {
+ case 'd':
+ key_state[Key_D] = Key_Up;
+ break;
+ case 'a':
+ key_state[Key_A] = Key_Up;
+ break;
+ case 'w':
+ key_state[Key_W] = Key_Up;
+ break;
+
+ case 's':
+ key_state[Key_S] = Key_Up;
+ break;
+ default:
+ break;
+ }
+ } break;
+ case ClientMessage: {
+ if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
+ quit = 1;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ vx = vy = 0;
+ if (key_state[Key_D] == Key_Down)
+ vx += 300;
+ if (key_state[Key_A] == Key_Down)
+ vx += -300;
+ if (key_state[Key_S] == Key_Down)
+ vy += 300;
+ if (key_state[Key_W] == Key_Down)
+ vy += -300;
+}
+</code></pre>
<p>
-RP2040のデータシート<sup>[3]</sup>「2.8.4.2 UF2 Format Details」を見ると、8バイト目のFlagsは、28バイト目にファミリーIDが書き込まれていることを示す<code>0x00002000</code>、12バイト目は、書き込みを行うフラッシュROMの先頭アドレスである<code>0x10000000</code>に、各ブロックの先頭からの位置を足したもの、16バイト目の、各ブロックのデータサイズは256バイト、28バイト目のファミリーIDは<code>0xe48bff56</code>である。あとは表の通り3つのマジックナンバーをセットし、32バイト目以降にデータを書き込み、20バイト目と24バイト目にブロックの通し番号と総数をそれぞれ書き込めばいい。ブロックの通し番号はデータのついでに書き込めるが、総数はデータを全部さばいた後でないと分からないので、最後全てのブロックにまとめて書き込むようにした。できたのが以下のコード:
+入力によって変更された速度は、<code>main()</code>関数内で次の座標を計算するために使用される:
</p>
-<pre><code>#include <stdio.h>
-#include <stdint.h>
+<pre><code>float px = 200, py = 200;
+float vx = 0, vy = 0;
+int width = 40, height = 40;
+
+int
+main(void)
+{
+ /* ... */
+ quit = 0;
+ while (!quit) {
+ handle_input()
+ /* ... */
+ px = px + vx * dt / 1000 / 1000 / 1000;
+ py = py + vy * dt / 1000 / 1000 / 1000;
+ // bind within the window
+ if (px < 0)
+ px = 0;
+ if (win_width < px + width)
+ px = win_width - width;
+ if (py < 0)
+ py = 0;
+ if (win_height < py + height)
+ py = win_height - height;
+
+ XClearArea(display, window,
+ 0, 0, // position
+ win_width, win_height, // width and height
+ False);
+ XFillRectangle(display, window, gc,
+ px, py, // position
+ width, height); // width and height
+ }
+ /* ... */
+}
+</code></pre>
+
+<h2>完成品</h2>
+<a href="https://git.mtkn.jp/xlib_playground/file/ex2/ex2.c.html">ソースコード</a>
+<p>色を変えてみた。</p>
+<video controls>
+ <source src="videos/ex2.webm" type="video/webm">
+</video>
+
+<h2>参考</h2>
+<ul>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+</ul>
+<p>次の記事: <a href="xlib_playground3.html">Xlibで遊んでみる3</a>
+</p>
+]]></description>
+</item>
+<item>
+<title>Xlibで遊んでみる1</title>
+<link>https://www.mtkn.jp/computer/xlib_playground1.html</link>
+<guid>https://www.mtkn.jp/computer/xlib_playground1.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Xlibで遊んでみる1</h1>
+<time>2022-12-21</time>
+
+<h2>はじめに</h2>
+<p>X11でGUIのプログラミングをしてみようと思い、してみた。X11用の低レベルのライブラリはXlibとxcbの二つがあるようだ。x.orgのウェブページを見てみると、Xlibは古く、xcbに置きかわりつつあるという。そのため、新しくなにかを作る場合はxcbを使うようにとのことである。ところがこのxcbはドキュメンテーションに乏しく、X11を触るのが初めての人間にはなにをどうすればいいのかほとんど分からなかった。知らない関数や構造体やらがでてきても(殆ど全部知らないものだが)、その関数なり構造体なりの説明がどこにも見当たらない。manページもない。あるのはdoxygenなるものでソースコードのコメントから自動生成したいい加減なものだけで、使いものにならない。</p>
+<p>とりあえずX11のことを少しは理解してからでないと初められそうもないと思い、もう少しましな情報があるXlibから始めることにした。</p>
+<p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。
+</p>
+
+<h2>初期設定</h2>
+<p>ディスプレイを開き、ウィンドウを作成する。変数はとりあえずグローバルに宣言することにした。<code>main</code>関数はできるだけ小さくして実際の処理はそれぞれの関数にさせてみる:</p>
+<pre><code>
+#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
+#include <X11/Xlib.h>
+/* macros */
+#define INIT_WIDTH 800
+#define INIT_HEIGHT 600
-size_t
-fwrite32l(uint32_t d, FILE *f)
+/* variables */
+Display *display;
+Window window;
+unsigned int win_width = INIT_WIDTH, win_height = INIT_HEIGHT;
+GC gc;
+Atom wm_delete_window;
+
+void
+setup(void)
{
- int i;
- uint8_t b;
- for (i = 0; i < 32; i += 8) {
- b = (uint8_t) (d >> i & 0xff);
- fwrite(&b, 1, 1, f);
- if (ferror(f)) {
- fprintf(stderr, "Fwrite32l: write error.\n");
- return 0;
- }
+ // Open a display.
+ if ((display = XOpenDisplay(NULL)) == NULL){
+ fprintf(stderr, "ERROR: could not open display\n");
+ exit(1);
}
- return 4;
+ // Create a window.
+ window = XCreateSimpleWindow(
+ display,
+ XDefaultRootWindow(display),
+ 0, 0,
+ win_width, win_height,
+ 0, 0, // border properties
+ 0); // background color: black
+ XStoreName(display, window, "UNKO");
+
+ // Setup a graphical context.
+ gc = XCreateGC(display, window, 0, NULL);
+ XSetForeground(display, gc, 0x0000FF);
+
+ // Kill the application when the window is destroyed.
+ wm_delete_window = XInternAtom(display,
+ "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(display, window, &wm_delete_window, 1);
+
+ // Setup which input to process.
+ XSelectInput(display, window,
+ ExposureMask|KeyPressMask|KeyReleaseMask);
+
+ // Actually draw the window.
+ XMapWindow(display, window);
+}
+
+void
+clean_up(void)
+{
+ XCloseDisplay(display);
}
+</code></pre>
+
+<p>適当な四角形のものを表示し、その位置を時間の関数として動かしてみる。</p>
+<pre><code>#include <time.h>
+#include <math.h>
int
-main(int argc, char *argv[])
+main(void)
{
- FILE *src = NULL, *dst = NULL;
- size_t sdata = 476;
- int retnum = 0;
+ int px, py;
+ int quit;
+ struct timespec ts;
+ XEvent event;
- uint32_t mag1 = 0x0A324655;
- uint32_t mag2 = 0x9E5D5157;
- uint32_t flags = 0x00002000; // familyID present
- uint32_t addr = 0x10000000;
- uint32_t nbyte = 256;
- uint32_t blk = 0;
- uint32_t nblk = 0;
- uint32_t famid = 0xe48bff56;
- uint8_t data[sdata];
- uint32_t mag3 = 0x0AB16F30;
+ setup();
+ quit = 0;
- memset(data, 0, sdata);
+ while (!quit){
+ while(XPending(display) > 0){
+ XNextEvent(display, &event);
+ switch (event.type){
+ case KeyPress: {
+ switch (XLookupKeysym(&event.xkey, 0)){
+ case 'q':
+ quit = 1;
+ break;
+ default:
+ break;
+ }
+ } break;
+ case ClientMessage: {
+ if ((Atom) event.xclient.data.l[0] == wm_delete_window) {
+ quit = 1;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ px = 200 + (int) (100 * sinf(ts.tv_sec + ts.tv_nsec / 1000.0 / 1000 / 1000));
+ py = 200 + (int) (100 * cosf(ts.tv_sec + ts.tv_nsec / 1000.0 / 1000 / 1000));
+ XClearArea(display, window,
+ 0, 0, // position
+ win_width, win_height, // width and height
+ False);
+ XFillRectangle(display, window, gc,
+ px, py, // position
+ 100, 100); // width and height
- if (argc != 3) {
- fprintf(stderr, "Usage: %s src dst\n", argv[0]);
- exit(1);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10 * 1000 * 1000;
+ nanosleep(&ts, NULL);
}
- if ((src = fopen(argv[1], "rb")) == NULL) {
- fprintf(stderr, "Could not open %s.\n", argv[1]);
- retnum = 1;
- goto defer;
- }
- if ((dst = fopen(argv[2], "wb")) == NULL) {
- fprintf(stderr, "Could not open %s.\n", argv[2]);
- retnum = 1;
- goto defer;
- }
-
- while (!feof(src)) {
- fwrite32l(mag1, dst);
- fwrite32l(mag2, dst);
- fwrite32l(flags, dst);
- fwrite32l(addr, dst);
- fwrite32l(nbyte, dst);
- fwrite32l(blk, dst);
- fwrite32l(nblk, dst); // dummy
- fwrite32l(famid, dst);
+ cleanup();
+ return 0;
+}
+</code></pre>
+
+<p>ここまでのコードはgitリポジトリの<a href="https://git.mtkn.jp/xlib_playground/file/ex1/ex1.c.html">ex1/ex1.c</a>にある。</p>
+<h2>完成品:</h2>
+<video controls>
+ <source src="videos/ex1.webm" type="video/webm">
+</video>
+
+<h2>参考</h2>
+<ul>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+<li><a href="https://www.youtube.com/watch?v=764fnfEb1_c">X11 App in C with Xlib(youtube video by tsoding)</a></li>
+</ul>
+
+<a href="xlib_playground2.html">次の記事</a>
+]]></description>
+</item>
+<item>
+<title>Arch Linuxのインストール</title>
+<link>https://www.mtkn.jp/computer/archlinux_installation.html</link>
+<guid>https://www.mtkn.jp/computer/archlinux_installation.html</guid>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>Arch Linuxのインストール</h1>
+<time>2021-03-25</time>
+
+<h2>ハードウェア構成</h2>
+
+<h2>インストールの準備</h2>
+
+<h3>インストールメディアの入手</h3>
+
+<h3>署名の検証</h3>
+<pre><code>$ gpg --keyserver-options auto-key-retrieve --verify archlinux-<i>version</i>-x86_64.iso.sig
+</code></pre>
- fread(data, 1, nbyte, src);
- if (ferror(src)) {
- fprintf(stderr, "Read error: %s.\n", argv[1]);
- retnum = 1;
- goto defer;
- }
- fwrite(data, 1, sdata, dst);
- if (ferror(src)) {
- fprintf(stderr, "Write error: %s.\n", argv[2]);
- retnum = 1;
- goto defer;
- }
+<h3>インストールメディアの準備</h3>
+<pre><code>$ sudo dd bs=4M if=<i>path/to/arch/linux/iso</i> of=/dev/sd<i>X</i> status=progress oflag=sync
+</code></pre>
- fwrite32l(mag3, dst);
+<h3>ライブ環境の起動</h3>
+Arch Linux install medium (x86_64, UEFI)を選択
- addr += nbyte;
- blk++;
- nblk++;
- }
+<h3>インストールの記録</h3>
+<pre><code># script install.log
+</code></pre>
- for (int i = 0; i < nblk; i++) {
- if (i == 0)
- if (fseek(dst, 24, SEEK_SET) < 0) {
- fprintf(stderr, "Seek error: %s.\n argv[2]");
- retnum = 1;
- goto defer;
- }
- fwrite32l(nblk, dst);
- if (i < nblk - 1)
- if(fseek(dst, 512 - 4, SEEK_CUR) < 0){
- fprintf(stderr, "Seek error: %s.\n argv[2]");
- retnum = 1;
- goto defer;
- }
- }
-
-defer:
- if (src)
- fclose(src);
- if (dst)
- fclose(dst);
- return retnum;
-}
+<h3>起動モードの確認</h3>
+<pre><code># ls /sys/firmware/efi/efivars
</code></pre>
-<p><code>fwrite32l()</code>関数は指定されたファイルに32ビットの整数を下位バイトから順に書き込む関数である。バイトオーダーとかややこしそうなので作っておいたけど必要なのかな?あと名前が気に入らない。</p>
-<p>
-CRC32のチェックサムが書き込まれたバイナリファイルを、このプログラムでUF2に変換し、生成されたファイルをUSBストレージとして接続したRP2040にコピーすればフラッシュROMに書き込まれる。
-</p>
+エラーが出なければUEFI。
-<h2>Flash Second Stage</h2>
+<h3>インターネットへ接続</h3>
<p>
-RP2040に電源を投入し、CRC32のチェックが通った後、フラッシュROMからコピーされたプログラムの先頭から実行が開始される。このコピーされた部分で、その後の動作に必要な各種の設定を行うことになる。RP2040のデータシートには、フラッシュROMとSSIコントローラのXIPを設定するようにと書かれている。XIPはExecute in Placeの略で、フラッシュROMの内容をCPUから直接実行するものである。SSIはSynchronous Serial Interfaceの略で、周辺機器と情報のやりとりをする通信方式である。RP2040はチップに内蔵されたこのSSIコントローラを通して、外部のフラッシュROMと通信しているのだが、このコントローラを適切に設定すればフラッシュROMの内容がCPUから直接アクセスできる<code>0x10000000</code>番地以降にマップされる。これによりフラッシュROMから内部のSRAMにデータをコピーすることなく命令を実行できるので、速くて便利だという。
+ネットワークインターフェイスが認識されているか確認:
</p>
+<pre><code># ip link
+</code></pre>
<p>
-しかしこのSSIコントローラはSynopsysという会社のDW_apb_ssiというIPを使っているようで、データシートのSSIコントローラの章は多分Synopsysの人が書いている。その他の章はRaspberry Pi財団の書いたブリティッシュイングリッシュだが、この部分だけ多分ネイティブじゃない人の書いたいい加減な英語である。誤植も多い。何日かかけて理解しようとしたがよく分からん。不毛なので一旦諦めた。</p>
-<p>
-RP2040には内部にもROMがあり、はバージョン情報や電源を投入した時の動作、その他便利な関数が書き込まれている。この関数の中に外部のフラッシュROMとSSIコントローラを設定するものも含まれているので、今回はこれを利用した。ただしこの方法だとフラッシュROMとの通信方式がStandard SPIのままなので少し遅いらしい。詳しくはデータシートの「2.3.8. Bootrom Contents」を参照。
+Wi-Fi接続:
</p>
+<pre><code># iwctl
+[iwd]# device list
+...
+[iwd]# exit
+</code></pre>
<p>
-RP2040の内蔵ROMの<code>0x00000018</code>番地に関数を検索するための関数がある。この関数に<code>0x00000014</code>番地の<code>rom_func_table</code>と、各関数に割り当てられた二文字の文字列を渡せば、欲しい関数へのポインタが返ってくる。なお、二文字の文字列はそれぞれASCIIコードで現し、二文字目を8ビットシフトしたものと1文字目のORを取ったものを渡すことになっている。今回欲しい関数はフラッシュROMをXIPに設定するもの(<code>_flash_enter_cmd_xip()</code>)なので、<code>'C', 'X'</code>を渡す。関数のポインタが返ってきて、それを呼び出せばフラッシュROMとSSIはXIPモードになる:
+接続を確認:
</p>
-<pre><code>setup_xip:
- ldr r3, rom_base
+<pre><code># ping archlinux.jp
+</code></pre>
- ldrh r0, [r3, #0x14] // rom_func_table
- ldr r1, =('C' | 'X' << 8) // _flash_enter_cmd_xip()
- ldrh r2, [r3, #0x18] // rom_table_lookup
- blx r2
- blx r0
-/* ... */
-rom_base:
- .word 0x00000000
+<h3>システムクロックの更新</h3>
+<pre><code># timedatectl set-ntp true
</code></pre>
-<p>
-XIPの設定が完了すれば、次はメインのプログラムを実行するための準備である。エントリーポイントの指定、スタックポインタの初期値の設定、ベクターテーブルの設定である。Armのマニュアル<sup>[7]</sup>によると、初期スタックポインタとエントリーポイントはベクターテーブルの<code>0x0</code>バイト目と<code>0x4</code>バイト目に書くことになっている:</p>
-<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/Exception-number-definition">
-<table>
-<caption>
-Table 7.3. Exception numbers
-</caption><colgroup><col><col></colgroup><thead><tr><th>Exception number</th><th>Exception</th></tr></thead><tbody><tr><td>1</td><td>Reset</td></tr><tr><td>2</td><td>NMI</td></tr><tr><td>3</td><td>HardFault</td></tr><tr><td>4-10</td><td>Reserved</td></tr><tr><td>11</td><td>SVCall</td></tr><tr><td>12-13</td><td>Reserved</td></tr><tr><td>14</td><td>PendSV</td></tr><tr><td>15</td><td>SysTick, optional</td></tr><tr><td>16</td><td>External Interrupt(0)</td></tr><tr><td>...</td><td>...</td></tr><tr><td>16 + N</td><td>External Interrupt(N)</td></tr></tbody>
-</table>
-</blockquote>
+<h3>パーティショニング</h3>
+<pre><code>sd<i>X</i>
+├sd<i>X</i>1 512M EFI System /boot
+└sd<i>X</i>2 lest Linux filesystem /
+</code></pre>
+<pre><code># lsblk
+# fdisk /dev/sd<i>X</i>
+Command (m for help): d
+...
+Command (m for help): w
+</code></pre>
-<blockquote cite="https://developer.arm.com/documentation/ddi0419/c/System-Level-Architecture/System-Level-Programmers--Model/ARMv6-M-exception-model/The-vector-table">
-<table>
-<caption>
-Table 7.4. Vector table format
-</caption><colgroup><col><col></colgroup><thead><tr><th>Word offset in table</th><th>Description, for all pointer address values</th></tr></thead><tbody><tr><td>0</td><td>SP_main. This is the reset value of the Main stack pointer.</td></tr><tr><td>Exception Number</td><td>Exception using that Exception Number</td></tr></tbody>
-</table>
-</blockquote>
-<p>
-また、ベクターテーブルはメインのプログラムの先頭に置くことにする。メインのプログラムはFlash Second Stageが占有する256バイトの直後、フラッシュROMの257バイト目から配置することにする。RP2040のベクターテーブルはM0PLUS: VTOR(<code>0xe0000000 + 0xed08</code>)というレジスタに書き込むことで設定する。以上をまとめると以下のコードになる:</p>
-<pre><code> ldr r0, flash_main
- ldr r1, m0plus_vtor
- str r0, [r1, #0] // vector table
- ldr r1, [r0, #4] // entry point
- ldr r0, [r0, #0] // stack pointer
- mov sp, r0
- bx r1
+<h3>パーティションのフォーマット</h3>
+<pre><code># mkfs.fat -F32 /dev/sd<i>X</i>1
+# mkfs.ext4 /dev/sd<i>X</i>2
+</code></pre>
-/* ... */
+<h3>ファイルシステムのマウント</h3>
+<pre><code># mount /dev/sd<i>X</i>2 /mnt
+# mkdir /mnt/boot
+# mount /dev/sd<i>X</i>1 /mnt/boot
+</code></pre>
-flash_main:
- .word 0x10000000 + 0x100
-m0plus_vtor:
- .word 0xe0000000 + 0xed08
+<h2>インストール</h2>
+<h3>ミラーの選択</h3>
+日本のサーバーを上に持ってくる:
+<pre><code># vim /etc/pacman.d/mirrorlist
</code></pre>
-<p>なお以上のコードは<code>.boot2</code>という名前のセクションにしてある。
-</p>
-<h2>メインのコード(<code>main.s</code>)</h2>
-<h3>ベクターテーブル</h3>
-<p>
-メインのコードの最初には上で説明したベクターテーブルを配置する。ここでは割り込みの処理は考えないので、初期スタックポインタとエントリーポイントだけである。初期スタックポインタはSRAMの最後?(<code>0x20040000</code>)、エントリーポイントはエントリーポイントのラベルを用いて設定した。</p>
-<pre><code>vectors:
- .word 0x20040000 // initial SP
- .word (reset+1)
+<h3>必須パッケージのインストール</h3>
+<pre><code># pacstrap /mnt base base-devel linux linux-firmware man-db man-pages
</code></pre>
-<p>
-<code>reset</code>ラベルに<code>1</code>を足しているのはRP2040がThumbモードのみに対応しているからである。ArmのCPUはArmモードとThumbモードがあり、Armモードは32ビットの命令で高機能。Thumbモードは16ビットの命令(一部32ビット)でコンパクトである。どちらのモードでも命令は2の倍数のアドレスに並ぶことになる。そのためジャンブ命令のジャンプ先のアドレスの最下位ビットは常に0である。この最下位ビットはジャンプ先のモードを示す為に利用される。両方のモードに対応したCPUではジャンプ先のアドレスの最下位ビットが0ならArmモード、1ならThumbモードに切り替わる。ブランチ命令のオペランド等は多分アセンブラがいい感じにしてくれるので単にラベルを書けば動く。ベクターテーブルのこの部分は自分で足す必要があるみたい。あんまりちゃんと調べてないのでマニュアル読んでや。</p>
-<p>
-この部分のセクション名は<code>.vectors</code>である。
-</p>
-<h3>GPIOの設定</h3>
-<p>
-電源投入直後、RP2040の周辺機器はリセット状態になっている。まずは今回利用するGPIOのリセット状態を解除する必要がある。データシートの「2.14. Subsystem Resets」には以下のように書かれている:
-</p>
-<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">
-<p>
-Every peripheral reset by the reset controller is held in reset at power-up.
-It is up to software to deassert the reset of peripherals it intends to use.
-</p>
-</blockquote>
+<h3>fstabの生成</h3>
+<pre><code># genfstab -U /mnt >> /mnt/etc/fstab
+</code></pre>
+
+<h3>chroot</h3>
+<pre><code># arch-chroot /mnt
+</code></pre>
+
+<h3>text editorをインストール</h3>
+<pre><code># pacman -S neovim
+</code></pre>
+
+<h3>タイムゾーン</h3>
+<pre><code># ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
+# hwclock --systohc
+</code></pre>
+
+<h3>ローカリゼーション</h3>
+<code>en_US.UTF-8</code>と<code>ja_JP.UTF-8</code>をコメントイン:
+<pre><code># nvim /etc/locale.gen
+</code></pre>
+ロケールを生成:
+<pre><code># locale-gen
+</code></pre>
+<pre><code># nvim /etc/locale.conf
+LANG=en_US.UTF-8
+</code></pre>
+
+<h3>ネットワーク設定</h3>
<p>
-リセット状態を解除するには、RESETS_BASE(<code>0x4000c000</code>)から<code>0x0</code>バイト目のRESETS: RESETレジスタのうち利用したい周辺機器のビットを<code>0x0</code>にすればいい。
-GPIOはIO Bank 0なので(これ明記されてなくない?)、RESETS: RESETレジスタのIO_BANK0(5番ビット)を<code>0x0</code>にする。
+無線接続用のソフトをインストール
</p>
-<h4>レジスタのアトミックなクリア</h4>
-<p>
-RESETS: RESETレジスタのうち5番ビットだけを<code>0x0</code>にしたい。この時、まずこのレジスタを読み込んでから<code>~(1 << 5)</code>と論理積を取って同レジスタに書き戻してもいいのだが、RP2040にはこれを一回の<code>str</code>でしかもアトミックにできる機能が用意されている。今回の場合アトミックかどうかは関係ないと思うけど。</p>
-<p>
-各レジスタには4個のアドレスが割り当てられている。データシートの各章のList of Registersに記載されているアドレスは通常の読み書きができる。そのアドレスに<code>0x1000</code>を足したものにアクセスするとアトミックなXORが、<code>0x2000</code>を足したものはアトミックなセットが、<code>0x3000</code>を足したものはアトミックなクリアができる。つまりレジスタのアドレスに<code>0x3000</code>を足したものに、<code>0x1 << 5</code>を<code>str</code>すれば5番目のビットだけ<code>0x0</code>にして、他のビットは変更されない。逆に指定したビットだけ立てて他を触らない場合は<code>0x2000</code>を、あるいは指定したビットだけトグルしたい場合は<code>0x1000</code>を足したアドレスにアクセスすればいい。</p>
-<h4>リセット状態の確認</h4>
-<p>リセットの解除はすぐに完了するわけではないようである。リセットの解除が完了したかどうか確認するにはRESETS: RESET_DONEレジスタ(RESETS_BASEから<code>0x8</code>バイト目)の該当するビット(ここでは5番目のビット)を読む。この値が<code>0x1</code>であればリセットの解除が完了している。<code>0x0</code>であれば処理が進行中なので<code>0x1</code>が返ってくるまで繰り返し読み込んで<code>0x0</code>になるまで待機する。ところでこのレジスタはリセットの解除が完了したかどうか確かめるものなので、RESET_DONEという名前はどうなん?
+<pre><code># pacman -S networkmanager
+</code></pre>
+
<p>
-以上から、GPIOのリセットを解除するのは以下のコード:
+ホストネームの設定
</p>
-<pre><code>reset:
- // unreset gpio
- mov r0, #1
- lsl r0, r0, #5 // io_bank0
- ldr r3, resets_base
- ldr r1, atomic_clr
- str r0, [r3, r1] // RESETS: RESET
-reset_chk:
- ldr r1, [r3, #0x8] // RESETS: RESET_DONE
- tst r0, r1
- beq reset_chk
-
-/* ... */
+<pre><code># nvim /etc/hostname
+<i>myhostname</i>
+</code></pre>
+<pre><code># nvim /etc/hosts
+127.0.0.1 localhost
+::1 localhost
+127.0.1.1 <i>myhostname</i>.localdomain <i>myhostname</i>
+</code></pre>
-atomic_clr:
- .word 0x00003000
-resets_base:
- .word 0x4000c000
+<h3>Rootパスワード</h3>
+<pre><code># passwd
</code></pre>
-<h3>GPIOの機能の選択</h3>
-<p>RP2040のGPIOにはそれぞれ複数の機能が用意されていて、どれを使うかはソフトウェアから選択できる。利用できる機能の一覧と各機能の説明はデータシートの「2.19.2 Function Select」に詳しく書いてある。ここではGPIO25番のピンをSIO(Single-cycle IO)として利用する。同じCPUが載っているRaspberry Pi PicoはGPIO25番にLEDが半田付けされている。25番にしたのはこれに合わせるためである。他のピンでもいい。GPIOに1か0を印加するだけならこのSIOを使うみたいである。Single-cycleはCPUから操作したときに1クロックでその操作が完了するという意味らしい(本当か)。SIOの詳しい説明はデータシートの「2.3.1 SIO」にある。</p>
+<h3>ブートローダー</h3>
<p>
-GPIO25番の機能を選択するにはIO_BANK0_BASE(<code>0x40014000</code>)から<code>0xcc</code>番目のGPIO25_CTRLレジスタの下位5ビットに、該当する機能の番号を書き込めばいい。データシートの「2.19.2 Function Select」にある表を見ると、GPIO25番のSIOは5である:</p>
-<pre><code> // set gpio functions
- ldr r3, io_bank0_base
- mov r0, #5 // sio
- mov r1, #0xcc
- str r0, [r3, r1] // IO_BANK0: GPIO25_CTRL
+インストール
+</p>
+<pre><code># pacman -S grub efibootmgr
+# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=boot
+</code></pre>
+<p>
+メイン設定ファイルの生成
+</p>
+<pre><code># grub-mkconfig -o /boot/grub/grub.cfg
+</code></pre>
-/* ... */
+<h3>マイクロコードのアップデートを有効化</h3>
+<pre><code># pacman -S intel-ucode
+# grub-mkconfig -o /boot/grub/grub.cfg
+上のん無駄やんけ
+</code></pre>
-io_bank0_base:
- .word 0x40014000
+<h2>再起動</h2>
+<pre><code># exit #chrootを抜ける
</code></pre>
-<h3>GPIOの出力を有効化</h3>
-<p>
-GPIO25番がSIOになったので、次にこのピンからの出力を有効化する。既定値では出力は無効になっている。ハイインピーダンスってことなのかな?出力を有効にするには、SIO_BASE(<code>0xd0000000</code>)から<code>0x24</code>バイト目のSIO: GPIO_OEレジスタの該当するビット(25番のピンなので25番ビット)を<code>0x1</code>にする:
-</p>
-<pre><code>
- // enable gpio output
- ldr r3, sio_base
- mov r0, #1
- lsl r0, r0, #25 // gpio25
- str r0, [r3, #0x24] // SIO: GPIO_OE
+<h3>インストールの記録を保存</h3>
+<pre><code># exit #scriptを停止
+# mv install.log /mnt/root
+</code></pre>
-/* ... */
+<h3>アンマウント</h3>
+<pre><code># umount -R /mnt
+</code></pre>
-sio_base:
- .word 0xd0000000
+<h3>再起動</h3>
+<pre><code># reboot
</code></pre>
-<h3>LEDの点滅</h3>
-<p>以上でGPIOの設定は完了したので、あとは実際にLEDに電圧を掛けるだけである。レジスタのアドレスに<code>0x1000</code>を足したものに書き込むとアトミックなレジスタのXORができると書いたが、SIOはこの機能がサポートされていないようである。データシートの「2.1.2 Atomic Register Access」に、
+<h2>インストール後</h2>
+<h3>システム管理</h3>
+<p>
+一般ユーザーの作成
</p>
-<blockquote cite="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">
+<pre><code># useradd -m -G wheel -s /bin/bash kenji
+# passwd kenji
+</code></pre>
<p>
-The SIO (Section 2.3.1), a single-cycle IO block attached directly to the cores'
-IO ports, does <strong>not</strong> support atomic accesses at the bus level,
-although some individual registers (e.g. GPIO) have set/clear/xor aliases.
+作成したユーザーをsudoerに追加
</p>
-</blockquote>
+<pre><code># visudo
+%wheel ALL=(ALL) ALL #uncomment
+</code></pre>
<p>
-と書かれている。そのかわりここにも書かれている通り、SIOの一部のレジスタにはアトミックなセット/クリア/XORをするためのレジスタが用意されている。ここではLEDを点滅させるためにGPIOの出力をトグルしたいのでXOR用のレジスタを使う。SIO_BASE(<code>0xd0000000</code>)から<code>0x1c</code>バイト目のSIO: GPIO_OUT_XORレジスタがそれである。このレジスタの25番ビットに<code>0x1</code>を書き込めばいい。出力をトグルした後は少し間をおいて同じことを繰り返す。間をおくためにここでは適当な数値を1づつ減らしていって0になったら返る関数<code>delay</code>を作った。タイマーと割り込みを使ったほうが消費電力等で優位なようだが、面倒なのでとりあえずこれで:</p>
+一般ユーザーとしてログインしなおす。
+</p>
-<pre><code> // blink led on gpio25
- ldr r4, sio_base
- mov r5, r0 // r0 = 1 << 25
-loop:
- str r5, [r4, #0x1c] // SIO: GPIO_OUT_XOR
- bl delay
- b loop
+<h3>パッケージ管理</h3>
+<p>
+AUR
+</p>
-delay:
- mov r0, #1
- lsl r0, r0, #20
-delay_loop:
- sub r0, r0, #1
- bne delay_loop
- bx lr
-/* ... */
-sio_base:
- .word 0xd0000000
+<h3>dotfilesを同期</h3>
+<p>
+gitのインストール
+</p>
+<pre><code>$ sudo pacman -S git
+$ git config --global user.name "<i>First-name</i> <i>Family-name</i>"
+$ git config --global user.email "<i>username</i>@<i>example.com</i>"
</code></pre>
-<p>なお以上のコードは<code>.text</code>セクションである。</p>
-
-<h2>リンカスクリプト</h2>
<p>
-以上のコードには<code>.boot2</code>、<code>.vectors</code>、<code>.text</code>の3つのセクションが含まれる。<code>.boot2</code>はフラッシュの先頭から256(<code>0x100</code>)バイト目まで、<code>.vectors</code>と<code>.text</code>はその後ろに続くように配置する:
-<pre><code>MEMORY
-{
- FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
-}
-
-SECTIONS
-{
- .boot2 : {
- *(.boot2)
- . = 0x100;
- } > FLASH
-
- .text : {
- *(.vectors)
- *(.text)
- } > FLASH
-}
+dotfilesを同期
+</p>
+<pre><code>$ mkdir ~/.local
+$ cd ~/.local
+$ git clone https://github.com/<i>dotfilesのリポジトリ</i>
+$ ln -sf ~/.local/dotfiles/.bash* ~/
+$ mkdir .config
+$ ln -s ~/.local/dotfiles/.config/* ~/.config/
+...
</code></pre>
-<h2>Makefile</h2>
+<h3>GUI</h3>
<p>
-以上のソースコードは以下のように配置している:
+グラフィックドライバのインストール
</p>
-<pre><code>rp2040
-├── ex1
-│ ├── Makefile
-│ ├── boot2.s
-│ ├── main.s
-│ └── memmap.ld
-└── tools
- ├── Makefile
- ├── bin2uf2.c
- └── bincrc.c
+<pre><code>$ sudo pacman -S nvidia nvidia-utils xorg-xinit
</code></pre>
<p>
-toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。ex1ディレクトリのMakefileは以下の通り:
+window managerをインストール
</p>
-<pre><code>AS = arm-none-eabi-as
-LD = arm-none-eabi-ld
-OBJCOPY = arm-none-eabi-objcopy
-BINCRC = ../tools/bincrc
-BIN2UF2 = ../tools/bin2uf2
-
-MCPU = -mcpu=cortex-m0plus
-ASFLAGS = $(MCPU)
-CFLAGS = $(MCPU) -ffreestanding -nostartfiles -O0 -fpic -mthumb -c
-LDFLAGS = --no-relax -nostdlib
-
-all: tools led.uf2
-
-clean:
- rm -f *.o *.elf *.uf2 *.bin
- cd ../tools && make clean
-
-.s.o:
- $(AS) $(ASFLAGS) -o $@ $<
+<pre><code>$ sudo pacman -S i3-wm i3blocks dmenu
+</code></pre>
+ドライバをインストールしたらxorg-server等も依存関係として入った。
+<p>
+フォントをインストール
+</p>
+<pre><code>$ sudo pacman -S noto-fonts-cjk noto-fonts-emoji ttf-joypixels ttf-font-awesome ttf-liberation
+</code></pre>
-led.elf: boot2.o main.o memmap.ld
- $(LD) $(LDFLAGS) -o $@ -T memmap.ld boot2.o main.o
+<p>
+ターミナル(st)をインストール
+</p>
+<pre><code>$ mkdir ~/.local/src
+$ cd ~/.local/src
+$ git clone git://git.suckless.org/st
+$ cd st
+$ sudo make install
+</code></pre>
-led.bin: led.elf
- $(OBJCOPY) -O binary led.elf $@
+<p>
+再起動
+</p>
+<pre><code># sudo reboot
+</code></pre>
+だめでした
+<p>
+ログイン時に次のエラー
+</p>
+<pre><code>xauth: error in locking authority file /home/kenji/.cache/X11/Xauthority
+</code></pre>
+<p>
+<code>~/.cache/X11</code>というディレクトリがないのが原因のようだ。
+<code>.bash_profile</code>でXauthorityの場所を変更していたのにディレクトリを作っていなかった。
+</p>
+<pre><code>$ mkdir ~/.cache/X11
+</code></pre>
+<p>
+として解決。
+</p>
-led.uf2: led.bin
- $(BINCRC) led.bin led_crc.bin
- $(BIN2UF2) led_crc.bin $@
+<h3>日本語入力</h3>
+<p>
+ibusとibus-skkをインストール
+</p>
+<pre><code>$ sudo pacman -S ibus ibu-skk skk-jisyo
+</code></pre>
+<pre><code>vim ~/.config/X11/xinitrc
+export DefaultIMModule=ibus
+export GTK_IM_MODULE=ibus
+export QT_IM_MODULE=ibus
+export XMODIFIERS="@im=ibus"
-flash: all
- mount /dev/disk/by-label/RPI-RP2 /mnt
- cp led.uf2 /mnt
+ibus-daemon --xim &
+</code></pre>
-tools:
- cd ../tools && make
+<h3>ブラウザ(brave)をインストール</h3>
+<pre><code>$ cd ~/.local/src
+$ git clone https://aur.archlinux.org/brave-bin.git
+$ cd brave-bin
+$ makepkg -si
</code></pre>
-<p>
-RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し、ex1ディレクトリで</p>
-<pre><code>$ make
-# make flash
+<h3>音</h3>
+<pre><code>$ sudo pacman -S alsa-utilst pulseaudio
+$ pulseaudio --start
</code></pre>
-<p>
-とすればプログラムがRP2040のボードに書き込まれて実行が開始される。</p>
-<h2>最後に</h2>
+
<p>
-光あれ。
+ssh
</p>
-
-<h2>参考</h2>
-<ul>
-<li>
-[1] Hennesy, J. L. and Patterson, D. A. 2017. Computer Organization And Design RISC-V Edition.
-</li>
-<li>
-[2] <a href="https://akizukidenshi.com/catalog/g/gK-17542/">RP2040マイコンボードキット.秋月電子通商</a>
-</li>
-<li>
-[3] <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a>
-</li>
-<li>
-[4] <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a>
-</li>
-<li>
-[5] <a href="https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB">巡回冗長検査.Wikipedia</a>
-</li>
-<li>
-[6] <a href="https://github.com/microsoft/uf2">USB Flashing Format (UF2).GitHub</a>
-</li>
-<li>
-[7] <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a>
-</li>
-</ul>
+<pre><code>$ sudo pacman -S openssh
+$ mkdir ~/.ssh
+$ cd ~/.ssh
+$ ssh-keygen -t rsa
+</code></pre>
]]></description>
</item>
<item>
diff --git a/pub/sitemap.xml b/pub/sitemap.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+<url><loc>https://www.mtkn.jp/computer/rp2040_1.html</loc><lastmod>2024-02-10</lastmod></url>
<url><loc>https://www.mtkn.jp/kitchen/recipe/miso.html</loc><lastmod>2024-02-09</lastmod></url>
<url><loc>https://www.mtkn.jp/kitchen/</loc><lastmod>2024-02-09</lastmod></url>
<url><loc>https://www.mtkn.jp/</loc><lastmod>2024-02-09</lastmod></url>
@@ -33,7 +34,6 @@
<url><loc>https://www.mtkn.jp/computer/xlib_playground2.html</loc><lastmod>2023-05-15</lastmod></url>
<url><loc>https://www.mtkn.jp/computer/xlib_playground1.html</loc><lastmod>2023-05-15</lastmod></url>
<url><loc>https://www.mtkn.jp/computer/archlinux_installation.html</loc><lastmod>2023-05-15</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/rp2040_1.html</loc><lastmod>2023-05-09</lastmod></url>
<url><loc>https://www.mtkn.jp/journal/posts/20230119.html</loc><lastmod>2023-01-19</lastmod></url>
<url><loc>https://www.mtkn.jp/farm/</loc><lastmod>2023-01-19</lastmod></url>
<url><loc>https://www.mtkn.jp/journal/posts/20221031.html</loc><lastmod>2022-10-31</lastmod></url>