commit 125868127edc9cdc73c4e8bfb7fd5b72f6357bdd
parent c17ca6f49466aeba3928ca7e6bd672db55e914fe
Author: Matsuda Kenji <info@mtkn.jp>
Date: Mon, 15 May 2023 19:48:57 +0900
fix html escape
Diffstat:
20 files changed, 1838 insertions(+), 1815 deletions(-)
diff --git a/data/weblog b/data/weblog
@@ -226,3 +226,17 @@
1683730800 /computer/rp2040_2.html
1683730800 /computer/rp2040_2.html
1683730800 /computer/rp2040_2.html
+1683730800 /computer/rp2040_2.html
+1684076400 /index.html
+1684076400 /computer/what-i-use.html
+1684076400 /computer/xlib_playground1.html
+1684076400 /computer/xlib_playground2.html
+1684076400 /computer/xlib_playground3.html
+1684076400 /computer/xlib_playground4.html
+1684076400 /computer/xlib_playground5.html
+1684076400 /computer/xlib_playground1.html
+1684076400 /computer/xlib_playground2.html
+1684076400 /computer/xlib_playground3.html
+1684076400 /computer/xlib_playground4.html
+1684076400 /computer/xlib_playground5.html
+1684076400 /computer/xlib_playground6.html
diff --git a/man/computer/what-i-use.html b/man/computer/what-i-use.html
@@ -30,6 +30,7 @@ UTF8に対応したnvi2を使っている。Arch Linuxだとちょっと使い
嫌い。JavaScriptがないと困るので仕方なく使っているが...</dd>
<dt>Mail Cdtent: neomutt</dt>
+<dd></dd>
</dl>
<h2>デスクトップ</h2>
@@ -49,6 +50,7 @@ OpenBSDで使えないので解雇した。あんまり使ってない。\
<dt>OS: OpenBSD</dt>
<dd>いい</dd>
<dt>ソフト: ノートパソコンと同じ</dt>
+<dd></dd>
</dl>
<h2>家のサーバー</h2>
@@ -59,6 +61,7 @@ OpenBSDで使えないので解雇した。あんまり使ってない。\
<dt>ハードウェア: Dell D520</dt>
<dd>おじいちゃんの家にころがってたのをもらってきた。</dd>
<dt>OS: OpenBSD</dt>
+<dd></dd>
</dl>
<h2>ウェブサーバー</h2>
diff --git a/man/computer/xlib_playground1.html b/man/computer/xlib_playground1.html
@@ -99,7 +99,7 @@ main(void)
quit = 0;
while (!quit){
- while(XPending(display) > 0){
+ while(XPending(display) > 0){
XNextEvent(display, &event);
switch (event.type){
case KeyPress: {
diff --git a/man/computer/xlib_playground2.html b/man/computer/xlib_playground2.html
@@ -34,14 +34,14 @@ main(void)
while (!quit) {
// fix fps
dt = 0;
- while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){
+ 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;
+ dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
}
// count fps.
fps_count++;
- if (t1 < t0){
+ if (t1 < t0){
printf("fps: %u\n", fps_count);
fps_count = 0;
}
@@ -108,7 +108,7 @@ void
handle_inputs(void)
{
XEvent event;
- while (XPending(display) > 0) {
+ while (XPending(display) > 0) {
XNextEvent(display, &event);
switch (event.type) {
case KeyPress: {
@@ -192,13 +192,13 @@ main(void)
px = px + vx * dt / 1000 / 1000 / 1000;
py = py + vy * dt / 1000 / 1000 / 1000;
// bind within the window
- if (px < 0)
+ if (px < 0)
px = 0;
- if (win_width < px + width)
+ if (win_width < px + width)
px = win_width - width;
- if (py < 0)
+ if (py < 0)
py = 0;
- if (win_height < py + height)
+ if (win_height < py + height)
py = win_height - height;
XClearArea(display, window,
diff --git a/man/computer/xlib_playground3.html b/man/computer/xlib_playground3.html
@@ -28,7 +28,7 @@ receive_events(int key_state[])
XEvent event;
XWindowAttributes wattr;
- while (XPending(display) > 0) {
+ while (XPending(display) > 0) {
XNextEvent(display, &event);
switch (event.type) {
case Expose: {
@@ -47,13 +47,13 @@ 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)
+ if (px < 0)
px = 0;
- if (win_width < px + width)
+ if (win_width < px + width)
px = win_width - width;
- if (py < 0)
+ if (py < 0)
py = 0;
- if (win_height < py + height)
+ if (win_height < py + height)
py = win_height - height;
}
</code></pre>
@@ -81,7 +81,7 @@ start_menu(void)
{
XEvent event;
char *menu_char_q = "press q to quit.";
- char *menu_char_s = "press <space> to start.";
+ char *menu_char_s = "press <space> to start.";
XClearArea(display, window,
0, 0, // position
diff --git a/man/computer/xlib_playground4.html b/man/computer/xlib_playground4.html
@@ -31,8 +31,8 @@ struct square {
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;
+ 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>
@@ -52,30 +52,30 @@ 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);
+ 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;
+ 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;
+ 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;
+ 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;
+ s1->py += lapy / 2;
+ s2->py -= lapy / 2;
}
}
}
-</pre></code>
+</code></pre>
<p>
衝突は弾性衝突として、衝突したそれぞれの四角形の速度を\
更新した。質量は四角形の面積として計算している。\
@@ -89,22 +89,22 @@ handle_collision_elastic(struct square *s1, struct square *s2)
return;
float v1, v2;
- float m1 = s1->w * s1->h;
- float m2 = s2->w * s2->h;
+ 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);
+ 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;
+ 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;
+ 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);
@@ -126,12 +126,12 @@ game_play(void)
/* ... */
while (next_menu == GAME_PLAY) {
/* ... */
- for (int j = 0; j < SUB_TICK; j++) {
- for (int i = 0; i < NUM_SQUARE; i++)
+ 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++) {
+ for (int i = 0; i < NUM_SQUARE; i++)
+ for (int j = i + 1; j < NUM_SQUARE; j++) {
handle_collision_elastic(&square[i], &square[j]);
/* ... */
}
diff --git a/man/computer/xlib_playground5.html b/man/computer/xlib_playground5.html
@@ -28,9 +28,9 @@ struct circle {
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);
+ 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>
@@ -45,16 +45,16 @@ circle_handle_collision_mm(struct circle *c1, struct circle *c2)
if (!circle_test_collision(c1, c2))
return;
- float col_px = c2->px - c1->px;
- float col_py = c2->py - c1->py;
+ 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;
- 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;
+ 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;
}
void
@@ -63,34 +63,34 @@ circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
if(!circle_test_collision(c1, c2))
return;
- float col_px = c2->px - c1->px;
- float col_py = c2->py - c1->py;
+ 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;
- float m1 = c1->m;
- float m2 = c2->m;
+ float m1 = c1->m;
+ float m2 = c2->m;
- float col_1v = c1->vx * col_px + c1->vy * col_py;
- float col_2v = c2->vx * col_px + c2->vy * col_py;
+ 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;
- 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);
+ 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;
+ 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);
}
diff --git a/man/computer/xlib_playground6.html b/man/computer/xlib_playground6.html
@@ -101,7 +101,7 @@ struct rect player;
/* ... */
int bi = 0;
- for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
+ 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;
@@ -138,13 +138,13 @@ handle_inputs(int key_state[])
return;
}
if (key_state[KEY_D] == KEY_DOWN) {
- if (player.vx > 0) {
+ if (player.vx > 0) {
player.ax = 500;
} else {
player.ax = 1000;
}
} else if (key_state[KEY_A] == KEY_DOWN) {
- if (player.vx > 0) {
+ if (player.vx > 0) {
player.ax = -1000;
} else {
player.ax = -500;
@@ -156,8 +156,8 @@ handle_inputs(int key_state[])
player.ax = -3 * player.vx;
}
- if (player.vx < -200) player.vx = -200;
- if (player.vx > 200) player.vx = 200;
+ 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;
}
@@ -170,24 +170,24 @@ handle_inputs(int key_state[])
<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;
+ 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 (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;
+ 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)
+ if (s->py > win_height)
next_menu = GAME_OVER;
}
</code></pre>
diff --git a/man/index.html b/man/index.html
@@ -14,7 +14,7 @@
<li>2023-04-25 <a href="/computer/what-i-use.html">使用しているハードウェア、ソフトウェア</a></li>
<li>2023-02-28 <a href="/poetry/index.html">詩</a></li>
<li>2023-01-25 <a href="/computer/xlib_playground6.html">Xlibで遊んでみる6</a></li>
-<li>2023-01-19 <a href="/journal/posts/20230119.html">寺を辞めた</a></li> </a></li>
+<li>2023-01-19 <a href="/journal/posts/20230119.html">寺を辞めた</a></li>
<li>2023-01-03 <a href="/computer/xlib_playground5.html">Xlibで遊んでみる5</a></li>
<li>2023-01-02 <a href="/computer/xlib_playground4.html">Xlibで遊んでみる4</a></li>
<li>2023-01-02 <a href="/computer/xlib_playground3.html">Xlibで遊んでみる3</a></li>
diff --git a/pub/computer/rp2040_2.html b/pub/computer/rp2040_2.html
@@ -54,7 +54,7 @@
<p>秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、より正確である。</p>
<h3>PLL</h3>
-<p>水晶振動子を入力として、周波数を数倍にしたものを出力するもの。電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」によると、入力周波数を<code>FREF</code>としたときの出力周波数は<code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。</p>
+<p>水晶振動子を入力として、周波数を数倍にしたものを出力するもの。電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」によると、入力周波数を<code>FREF</code>としたときの出力周波数は<code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。これらの変数はそれぞれ設定用のレジスタに値を保存することで変更できる。</p>
<h2>UART</h2>
diff --git a/pub/computer/what-i-use.html b/pub/computer/what-i-use.html
@@ -43,6 +43,7 @@
<dd>もうちょっとましなのないんかな。UIころころ変わるし、重いし、嫌い。JavaScriptがないと困るので仕方なく使っているが...</dd>
<dt>Mail Cdtent: neomutt</dt>
+<dd></dd>
</dl>
<h2>デスクトップ</h2>
@@ -59,6 +60,7 @@
<dt>OS: OpenBSD</dt>
<dd>いい</dd>
<dt>ソフト: ノートパソコンと同じ</dt>
+<dd></dd>
</dl>
<h2>家のサーバー</h2>
@@ -69,6 +71,7 @@
<dt>ハードウェア: Dell D520</dt>
<dd>おじいちゃんの家にころがってたのをもらってきた。</dd>
<dt>OS: OpenBSD</dt>
+<dd></dd>
</dl>
<h2>ウェブサーバー</h2>
diff --git a/pub/computer/xlib_playground1.html b/pub/computer/xlib_playground1.html
@@ -106,7 +106,7 @@ main(void)
quit = 0;
while (!quit){
- while(XPending(display) > 0){
+ while(XPending(display) > 0){
XNextEvent(display, &event);
switch (event.type){
case KeyPress: {
diff --git a/pub/computer/xlib_playground2.html b/pub/computer/xlib_playground2.html
@@ -49,14 +49,14 @@ main(void)
while (!quit) {
// fix fps
dt = 0;
- while (dt < 1.0 * 1000 * 1000 * 1000 / FPS){
+ 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;
+ dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1000 * 1000 * 1000;
}
// count fps.
fps_count++;
- if (t1 < t0){
+ if (t1 < t0){
printf("fps: %u\n", fps_count);
fps_count = 0;
}
@@ -105,7 +105,7 @@ void
handle_inputs(void)
{
XEvent event;
- while (XPending(display) > 0) {
+ while (XPending(display) > 0) {
XNextEvent(display, &event);
switch (event.type) {
case KeyPress: {
@@ -188,13 +188,13 @@ main(void)
px = px + vx * dt / 1000 / 1000 / 1000;
py = py + vy * dt / 1000 / 1000 / 1000;
// bind within the window
- if (px < 0)
+ if (px < 0)
px = 0;
- if (win_width < px + width)
+ if (win_width < px + width)
px = win_width - width;
- if (py < 0)
+ if (py < 0)
py = 0;
- if (win_height < py + height)
+ if (win_height < py + height)
py = win_height - height;
XClearArea(display, window,
diff --git a/pub/computer/xlib_playground3.html b/pub/computer/xlib_playground3.html
@@ -44,7 +44,7 @@ receive_events(int key_state[])
XEvent event;
XWindowAttributes wattr;
- while (XPending(display) > 0) {
+ while (XPending(display) > 0) {
XNextEvent(display, &event);
switch (event.type) {
case Expose: {
@@ -63,13 +63,13 @@ 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)
+ if (px < 0)
px = 0;
- if (win_width < px + width)
+ if (win_width < px + width)
px = win_width - width;
- if (py < 0)
+ if (py < 0)
py = 0;
- if (win_height < py + height)
+ if (win_height < py + height)
py = win_height - height;
}
</code></pre>
@@ -92,7 +92,7 @@ start_menu(void)
{
XEvent event;
char *menu_char_q = "press q to quit.";
- char *menu_char_s = "press <space> to start.";
+ char *menu_char_s = "press <space> to start.";
XClearArea(display, window,
0, 0, // position
diff --git a/pub/computer/xlib_playground4.html b/pub/computer/xlib_playground4.html
@@ -48,8 +48,8 @@
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;
+ 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>
@@ -62,30 +62,30 @@ 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);
+ 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;
+ 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;
+ 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;
+ 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;
+ s1->py += lapy / 2;
+ s2->py -= lapy / 2;
}
}
}
-</pre></code>
+</code></pre>
<p>
衝突は弾性衝突として、衝突したそれぞれの四角形の速度を更新した。質量は四角形の面積として計算している。衝突後の速度はエネルギー保存則と運動量保存則から導いたのでしんどかった。
</p>
@@ -96,22 +96,22 @@ handle_collision_elastic(struct square *s1, struct square *s2)
return;
float v1, v2;
- float m1 = s1->w * s1->h;
- float m2 = s2->w * s2->h;
+ 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);
+ 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;
+ 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;
+ 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);
@@ -130,12 +130,12 @@ game_play(void)
/* ... */
while (next_menu == GAME_PLAY) {
/* ... */
- for (int j = 0; j < SUB_TICK; j++) {
- for (int i = 0; i < NUM_SQUARE; i++)
+ 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++) {
+ for (int i = 0; i < NUM_SQUARE; i++)
+ for (int j = i + 1; j < NUM_SQUARE; j++) {
handle_collision_elastic(&square[i], &square[j]);
/* ... */
}
diff --git a/pub/computer/xlib_playground5.html b/pub/computer/xlib_playground5.html
@@ -47,9 +47,9 @@
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);
+ 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>
@@ -63,16 +63,16 @@ circle_handle_collision_mm(struct circle *c1, struct circle *c2)
if (!circle_test_collision(c1, c2))
return;
- float col_px = c2->px - c1->px;
- float col_py = c2->py - c1->py;
+ 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;
- 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;
+ 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;
}
void
@@ -81,34 +81,34 @@ circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
if(!circle_test_collision(c1, c2))
return;
- float col_px = c2->px - c1->px;
- float col_py = c2->py - c1->py;
+ 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;
- float m1 = c1->m;
- float m2 = c2->m;
+ float m1 = c1->m;
+ float m2 = c2->m;
- float col_1v = c1->vx * col_px + c1->vy * col_py;
- float col_2v = c2->vx * col_px + c2->vy * col_py;
+ 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;
- 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);
+ 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;
+ 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);
}
diff --git a/pub/computer/xlib_playground6.html b/pub/computer/xlib_playground6.html
@@ -117,7 +117,7 @@ struct rect player;
/* ... */
int bi = 0;
- for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
+ 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;
@@ -151,13 +151,13 @@ handle_inputs(int key_state[])
return;
}
if (key_state[KEY_D] == KEY_DOWN) {
- if (player.vx > 0) {
+ if (player.vx > 0) {
player.ax = 500;
} else {
player.ax = 1000;
}
} else if (key_state[KEY_A] == KEY_DOWN) {
- if (player.vx > 0) {
+ if (player.vx > 0) {
player.ax = -1000;
} else {
player.ax = -500;
@@ -169,8 +169,8 @@ handle_inputs(int key_state[])
player.ax = -3 * player.vx;
}
- if (player.vx < -200) player.vx = -200;
- if (player.vx > 200) player.vx = 200;
+ 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;
}
@@ -180,24 +180,24 @@ handle_inputs(int key_state[])
<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;
+ 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 (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;
+ 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)
+ if (s->py > win_height)
next_menu = GAME_OVER;
}
</code></pre>
diff --git a/pub/index.html b/pub/index.html
@@ -37,7 +37,7 @@
<li>2023-04-25 <a href="/computer/what-i-use.html">使用しているハードウェア、ソフトウェア</a></li>
<li>2023-02-28 <a href="/poetry/index.html">詩</a></li>
<li>2023-01-25 <a href="/computer/xlib_playground6.html">Xlibで遊んでみる6</a></li>
-<li>2023-01-19 <a href="/journal/posts/20230119.html">寺を辞めた</a></li> </a></li>
+<li>2023-01-19 <a href="/journal/posts/20230119.html">寺を辞めた</a></li>
<li>2023-01-03 <a href="/computer/xlib_playground5.html">Xlibで遊んでみる5</a></li>
<li>2023-01-02 <a href="/computer/xlib_playground4.html">Xlibで遊んでみる4</a></li>
<li>2023-01-02 <a href="/computer/xlib_playground3.html">Xlibで遊んでみる3</a></li>
diff --git a/pub/rss.xml b/pub/rss.xml
@@ -5,893 +5,1010 @@
<description>ウェブページの更新履歴</description>
<language>ja-jp</language>
<link>https://www.mtkn.jp</link>
-<lastBuildDate>Thu, 11 May 2023 19:35:49 +0900</lastBuildDate>
-<pubDate>Thu, 11 May 2023 19:35:49 +0900</pubDate>
+<lastBuildDate>Mon, 15 May 2023 19:48:43 +0900</lastBuildDate>
+<pubDate>Mon, 15 May 2023 19:48:43 +0900</pubDate>
<docs>https://www.rssboard.org/rss-specification</docs>
<item>
-<title>RP2040 SDKなし2 Clock, UART</title>
-<link>https://www.mtkn.jp/computer/rp2040_2.html</link>
-<guid>https://www.mtkn.jp/computer/rp2040_2.html</guid>
-<pubDate>Thu, 11 May 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>RP2040 SDKなし2 Clock, UART</h1>
-<time>2023-05-10</time>
-<p>
-前回: <a href="rp2040_1.html">RP2040 SDKなしでLチカ</a><br>
-ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex2
-</p>
-
-<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>
-</ul>
-
-<h2>Clock</h2>
-
-<h3>リング発振回路</h3>
-<p>RP2040にはリング発振回路というのが内蔵されている。これは自分の出力を反転させようとするもので、不安定だが高速で消費電力の少ないクロックとして用いられる。RP2040は電源を入れると、このリング発振回路を動作用のクロックとして用いている。この発振回路の周波数は、チップの製造過程での誤差、動作時の電圧、動作温度によって変動するので、正確な周波数が必要な用途には向かない。</p>
-
-<h3>水晶発振子</h3>
-<p>秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、より正確である。</p>
-
-<h3>PLL</h3>
-<p>水晶振動子を入力として、周波数を数倍にしたものを出力するもの。電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」によると、入力周波数を<code>FREF</code>としたときの出力周波数は<code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。</p>
-
-<h2>UART</h2>
-
-<h2>リング発振回路でUARTは動くんかな?</h2>
-<p>UARTの通信には正確なクロックが必要である。その為上では<code>clk_peri</code>として水晶発振子とPLLを用いた。ところがpico-examplesのhello_uartでは<code>main()</code>関数で水晶発振子を設定していない。そこでリング発振回路を用いてみたのだが、どうもうまく通信できない。出力されている正確な周波数も分からないのであきらめることにした。オシロスコープなんていうものは持っていない。</p>
+<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>
-<h3>pico-sdk</h3>
-<p>
-ところがどうも調べているとSDKを使った場合、デフォルトではクロック周波数は125MHzになっているらしい。どうやら水晶発振子もPLLも<code>main()</code>が呼ばれる前に設定されているようである。</p>
<p>
-pico-examplesのサンプルプログラムはビルドすると自動で逆アセンブリしたファイルを出力してくれる。これを見ると、最初の256バイトは前回説明したboot2のコードで、その後ろにベクターテーブルが続く。ベクターテーブルの最初は初期スタックポインタで、<code>0x20042000</code>になっている。次はエントリーポイントで、<code>0x100001f7</code>である:</p>
-<pre><code>10000100 <__VECTOR_TABLE>:
-10000100: 20042000 .word 0x20042000
-10000104: 100001f7 .word 0x100001f7
-</code></pre>
+前回: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
+</p>
<p>
-Thumbモードなので実際のエントリーポイントは<code>1</code>引いた、<code>0x100001f6</code>である。この場所ではまず自分のCPUIDを調べて、<code>1</code>であれば待機状態に移行する。RP2040はデュアルコアである。起動直後はCPUIDが<code>0</code>のコアだけで処理をして、CPUIDが<code>1</code>のコアはプログラマが必要に応じて起動することになっている。このためCPUIDが<code>1</code>のコアは起動してすぐに待機状態に入ることがデータシートに書かれている。しかしこの処理はユーザーの書いたプログラムじゃなくて内蔵ROMにある起動用プログラムが担当するみたいに書かれてるんやけど、なんでSDKではユーザープログラムの一部として組み込んでるんかな?
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
</p>
-<pre><code>100001f6 <_reset_handler>:
-100001f6: 481d ldr r0, [pc, #116] ; (1000026c <hold_non_core0_in_bootrom+0xe>)
-100001f8: 6800 ldr r0, [r0, #0]
-100001fa: 2800 cmp r0, #0
-100001fc: d12f bne.n 1000025e <hold_non_core0_in_bootrom>
-</code></pre>
-<p>上のコードの最初の<code>ldr</code>は、<code>0xd0000000</code>(M0PLUS: CPUIDレジスタ)をロードしている。最後の飛び先<code>0x1000025e</code>はCPUIDが<code>1</code>のCPUを待機させる処理である:</p>
-<pre><code>1000025e <hold_non_core0_in_bootrom>:
-1000025e: 4809 ldr r0, [pc, #36] ; (10000284 <hold_non_core0_in_bootrom+0x26>)
-10000260: f001 fb9c bl 1000199c <rom_func_lookup>
-10000264: 4700 bx r0
-10000266: 0000 .short 0x0000
-/* ... */
-10000284: 00005657 .word 0x00005657
-</code></pre>
-<p>内蔵フラッシュに書きこまれた関数を呼びだしている。呼びだしに使うコードは<code>0x00005657</code>(<code>'W' | 'V' << 8</code>)である。データシートを見ると、この関数は<code>_wait_for_vector()</code>という名前で、CPUIDが1のCPUを寝かしつけるのに使われると書いている。この部分のソースコードをpico-sdkで探すと<code>pico-sdk/src/rp2_common/pico_standard_link/crt0.S</code>というのが見付かった:</p>
-<pre><code>$ find pico-sdk/src -type f | xargs grep -l _reset_handler
-pico-sdk/src/rp2_common/pico_standard_link/crt0.S
-</code></pre>
-<p>このファイルによると:
+
+<h2>ワールドマップの作成</h2>
+<p>
+ゲームのワールドマップを作製した。ここでは文字列として登録した。なにもないところは「<code>.</code>」、ブロックの場所は「<code>b</code>」、プレーヤーは「<code>p</code>」とした:
</p>
-<pre><code> // Only core 0 should run the C runtime startup code; core 1 is normally
- // sleeping in the bootrom at this point but check to be sure
+<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>だそうである。やっぱり無駄やん。内蔵フラッシュのプログラムにバグがあってもこのコードのせいで見付かりにくくなってない?知らんけど。</p>
-<p>続いて<code>.data</code>領域と<code>.bss</code>領域のコピー、初期化のようである。多分OSの本かなんかで習ったメモリマップの話:</p>
-<pre><code>100001fe: a40d add r4, pc, #52 ; (adr r4, 10000234 <data_cpy_table>)
-10000200: cc0e ldmia r4!, {r1, r2, r3}
-10000202: 2900 cmp r1, #0
-10000204: d002 beq.n 1000020c <_reset_handler+0x16>
-10000206: f000 f812 bl 1000022e <data_cpy>
-1000020a: e7f9 b.n 10000200 <_reset_handler+0xa>
-1000020c: 4918 ldr r1, [pc, #96] ; (10000270 <hold_non_core0_in_bootrom+0x12>)
-1000020e: 4a19 ldr r2, [pc, #100] ; (10000274 <hold_non_core0_in_bootrom+0x16>)
-10000210: 2000 movs r0, #0
-10000212: e000 b.n 10000216 <bss_fill_test>
-
-10000214 <bss_fill_loop>:
-10000214: c101 stmia r1!, {r0}
-
-10000216 <bss_fill_test>:
-10000216: 4291 cmp r1, r2
-10000218: d1fc bne.n 10000214 <bss_fill_loop>
+<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;
-<p>最後にいろいろ呼びだす:</p>
-<pre><code>1000021a <platform_entry>:
-1000021a: 4917 ldr r1, [pc, #92] ; (10000278 <hold_non_core0_in_bootrom+0x1a>)
-1000021c: 4788 blx r1
-1000021e: 4917 ldr r1, [pc, #92] ; (1000027c <hold_non_core0_in_bootrom+0x1e>)
-10000220: 4788 blx r1
-10000222: 4917 ldr r1, [pc, #92] ; (10000280 <hold_non_core0_in_bootrom+0x22>)
-10000224: 4788 blx r1
-10000226: be00 bkpt 0x0000
-10000228: e7fd b.n 10000226 <platform_entry+0xc>
/* ... */
-10000278: 10001819 .word 0x10001819
-1000027c: 100002dd .word 0x100002dd
-10000280: 10001909 .word 0x10001909
-</code></pre>
-<p>一つめの<code>blx</code>は<code>0x10001818</code>(<code>runtime_init</code>)を、二つめは<code>0x100002dc</code>(<code>main</code>)を、最後のは<code>0x10001908</code>(<code>exit</code>)を、それぞれ呼んでいる。この<code>runtime_init</code>はアセンブリでは分かりにくいのでソースコードを探してみると、以下のものが見付かった:</p>
-<pre><code>$ find pico-sdk/src -type f | xargs grep -l runtime_init
-pico-sdk/src/rp2_common/pico_runtime/runtime.c
-pico-sdk/src/rp2_common/pico_standard_link/crt0.S
-pico-sdk/src/common/pico_sync/include/pico/mutex.h
-</code></pre>
-<p>最後の<code>mutex.h</code>は関係なさそう。二つめの<code>crt0.S</code>は呼びだしてるだけ。一つめの<code>runtime.c</code>が多分探しているものである。これを見るとまず各種周辺機器を一度リセットし、リセット状態を解除している。使わんやつも初期化してない?その後<code>clocks_init()</code>を呼んでいる。この関数は<code>pico-sdk/src/rp2_common/hardware_clocks/clocks.c</code>で定義されている。これを見ると、<code>xosc_init()</code>を呼んで水晶発振子を初期化した後、<code>clk_peri</code>を125MHzに設定している:</p>
-<pre><code> clock_configure(clk_peri,
- 0,
- CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
- 125 * MHZ,
- 125 * MHZ);
-</code></pre>
-<p>やっぱり水晶発振子じゃないとあかんのかな。</p>
-
-<h2>CMake</h2>
-<p>上ではビルドしたバイナリを逆アッセンブルして読んだ。わざわざこんなことをしなくてもMakefile読めばなにがどうなって最終生成物に辿りつくのか分かればいいのだが、そうもいかない。このSDKとpico-examplesにはビルドシステムとしてCMakeなるものが使われている。これがどうも複雑でよく分からない。勉強する気にもならん。上で見た<code>crt0.S</code>や<code>runtime.c</code>といったファイルも<code>hello_uart</code>で本当に使われているものなのかもよく分からない。こんな煩雑なものは本当に必要なのかな。無駄に複雑にしてるだけとちゃうんかな。特に僕は勉強用に使ってるので、ソースコードの依存関係をもっと分かりやすくしてくれないと、内部でなにがどうなってるのか理解しにくい。何度か頑張って読もうとしたが、面白くないのでやめた。数百行のファイルをあっちからこっちから<code>include</code>してるし、大文字ばかりの変数だらけで目が痛い。こんなものを扱えるというのはえらいええ頭してはるんやね<sup>†</sup>。</p>
+ 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>
-<ul>
-<li>
-<a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a>
-</li>
-<li>
-<a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a>
-</li>
-<li>
-<a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a>
-</li>
-<li>
-<a href="https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%82%B7%E3%83%AC%E3%83%BC%E3%82%BF">リング・オシレータ.Wikipedia</a>
-</li>
-<li>
-<a href="https://www5.epsondevice.com/ja/information/technical_info/osc.html">水晶発振器とは? 原理と仕組み、水晶振動子との違い、選び方のポイントを解説.エプソン水晶デバイス</a>
-</li>
-</ul>
+<p>ユーザーからの入力を受けとり、プレイーヤの加速度等を変更。<code>A</code>、<code>D</code>でそれぞれ左右に加速し、地面に接しているときに<code>space</code>キーでジャンプさせる:
+</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;
+ }
-<p><sup>†</sup>僕は和歌山の人間である。</p>
+ 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>
-]]></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>
+<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;
-<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>
-<p>
-ということでSDKなしで開発してみる。とりあえず定番のLチカをば。</p>
-<p>
-ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>
-</p>
+ // 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>
-<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>
-</ul>
-<h2>Boot Process</h2>
+<h2>完成品</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="https://git.mtkn.jp/xlib_playground/file/ex6/ex6.c.html">git</a>
</p>
<p>
-以上のことから、プログラムを実行するためにはCRC32を計算し、UF2という形式に変換することが必要である。ソースコードからの流れは以下の通り:
+<video controls>
+<source src="videos/ex6.webm" type="video/webm">
+</video>
</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>
-<p>
-らしい。
-</p>
-<p>
-入力のビットを一列に並べて、除数で「割り算」していく。この「割り算」が多項式の除算に似ているので、この除数を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>
-普通の割り算と基本は同じであるが、引き算の部分だけXORになっている。</p>
-<p>
-以上の計算をプログラムの先頭252バイトに対して、33ビットの除数を用いて行う。データの並べ方は、上の例において左側を先頭に、フラッシュROM上の0番地から、各バイトは最上位ビットから順に並べる。入力のデータは253バイト目から256バイト目に<code>0</code>をひっつけて計算する。これは多分予め長さが分からないデータでも計算できるようにしたかったからかな。除数は<code>0x104c11db7</code>である(最上位ビットは常に1なのでデータシートでは省略されている)。</p>
-<p>
-入力データは1バイトづつ処理したいみたいである。多分通信等で使う都合である。この時XORは結合則が成り立つので1バイト処理した結果と次のバイトとをXORして次の処理の入力として利用することができる:
-</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>
+<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>だけで行うことを考える:
-</p>
-<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
+]]></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>
-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バイト目の結果
+<p>
+前回: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
+</p>
+<p>
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+</p>
-10000001 入力の2バイト目
--------------------------------- XOR
-00000111111000101011111100010100
-先頭1ビットが0なので1ビットシフト
--------------------------------- シフト
-00001111110001010111111000101000
-.
-.
-.
+<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
+};
+
+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>
-これを実装したのが以下のコード:</p>
-<pre><code>uint32_t
-crc32(uint8_t *idata, size_t len)
+衝突後は前回と同じく弾性衝突として処理した。四角形とは違い、衝突方向の場合分けが不要なので楽である。
+</p>
+<pre><code>
+void
+circle_handle_collision_mm(struct circle *c1, struct circle *c2)
{
- uint32_t pol = 0x04C11DB7;
- uint32_t c = 0xFFFFFFFF;
- uint32_t b;
+ if (!circle_test_collision(c1, c2))
+ return;
- 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;
- }
- }
+ 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;
- return c;
+ 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;
+}
+
+void
+circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
+{
+ if(!circle_test_collision(c1, c2))
+ return;
+
+ 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;
+
+ float m1 = c1->m;
+ float m2 = c2->m;
+
+ 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;
+
+ 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>
+
+<h2>完成品</h2>
<p>
-<code>main()</code>関数では上の<code>crc32()</code>に、<code>idata</code>として入力となるバイナリデータの先頭を、<code>len</code>として<code>252</code>を渡してCRC32を計算させる。その後、出力先のファイルに入力元のデータをコピーしていき、253バイト目から256バイト目だけ、計算したCRC32に置き換える。入力元のこの場所にデータが書き込まれていないかどうかは確かめていない。
+<a href="https://git.mtkn.jp/xlib_playground/file/ex5/ex5.c.html">git</a>
+</p>
+<p>
+<video controls>
+<source src="videos/ex5.webm" type="video/webm">
+</video>
</p>
-<h2>UF2(USB Flashing Format)</h2>
+<h2>参考</h2>
+<ul>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+</ul>
<p>
-Microsoftが開発したフラッシュ書き込み用のファイル形式らしい:
-<blockquote cite="https://github.com/microsoft/uf2">
+次の記事: <a href="xlib_playground6.html">Xlibで遊んでみる6</a>
+</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>
+
<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>.
+前回: <a href="xlib_playground3.html">Xlibで遊んでみる3</a>
</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>
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+</p>
+<h2>衝突判定とその処理</h2>
<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>
+<p>
+回転しない四角形どうしの衝突判定は簡単である。x軸方向とy軸方向の両方に重なりがあれば衝突している:
</p>
-<pre><code>#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
+<pre><code>struct square {
+ float ppx, ppy; // previous position
+ float px, py; // current position
+ float vx, vy; // velocity
+ int w, h; // width and height
+};
-size_t
-fwrite32l(uint32_t d, FILE *f)
+int
+test_collision(struct square *s1, struct square* s2)
{
- 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;
+ 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>
-int
-main(int argc, char *argv[])
+<p>
+衝突後の処理は多少めんどくさかった。衝突した時は既にめりこんでいるので、まずはそれぞれをめりこんだ距離の半分ずつずらして衝突を解消するようにした。この際、x軸方向にぶつかったのか、y軸方向にぶつかったのかで、それぞれの軸方向にひっぺがすようにしている。二つの四角形の各軸に関するめりこんだ距離<code>lapx</code>、<code>lapy</code>と各軸に関する相対速度<code>rel_vx</code>、<code>rel_vy</code>の比を比べればどちらの軸方向にぶつかったかが分かるはずである、多分 :
+</p>
+<pre><code>void
+handle_collision_mm(struct square *s1, struct square *s2)
{
- FILE *src = NULL, *dst = NULL;
- size_t sdata = 476;
- int retnum = 0;
-
- 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;
-
- memset(data, 0, sdata);
-
- if (argc != 3) {
- fprintf(stderr, "Usage: %s src dst\n", argv[0]);
- exit(1);
- }
+ if (!test_collision(s1, s2))
+ return;
- 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);
+ 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);
- fread(data, 1, nbyte, src);
- if (ferror(src)) {
- fprintf(stderr, "Read error: %s.\n", argv[1]);
- retnum = 1;
- goto defer;
+ 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;
}
- fwrite(data, 1, sdata, dst);
- if (ferror(src)) {
- fprintf(stderr, "Write error: %s.\n", argv[2]);
- retnum = 1;
- goto defer;
+ } 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;
- fwrite32l(mag3, dst);
+ float v1, v2;
+ float m1 = s1->w * s1->h;
+ float m2 = s2->w * s2->h;
- addr += nbyte;
- blk++;
- nblk++;
+ 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;
}
- 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;
- }
+ 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]);
+ /* ... */
+ }
+ /* ... */
+ }
+ /* ... */
}
-
-defer:
- if (src)
- fclose(src);
- if (dst)
- fclose(dst);
- return retnum;
+ /* ... */
}
</code></pre>
-<p><code>fwrite32l()</code>関数は指定されたファイルに32ビットの整数を下位バイトから順に書き込む関数である。バイトオーダーとかややこしそうなので作っておいたけど必要なのかな?あと名前が気に入らない。</p>
+
+<h2>完成品</h2>
<p>
-CRC32のチェックサムが書き込まれたバイナリファイルを、このプログラムでUF2に変換し、生成されたファイルをUSBストレージとして接続したRP2040にコピーすればフラッシュROMに書き込まれる。
+<a href="https://git.mtkn.jp/xlib_playground/file/ex4/ex4.c.html">git</a>
+</p>
+<p>
+<video controls>
+<source src="videos/ex4.webm" type="video/webm">
+</video>
</p>
-<h2>Flash Second Stage</h2>
+<h2>参考</h2>
+<ul>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+</ul>
<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にデータをコピーすることなく命令を実行できるので、速くて便利だという。
+次の記事: <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>
+
<p>
-しかしこのSSIコントローラはSynopsysという会社のDW_apb_ssiというIPを使っているようで、データシートのSSIコントローラの章は多分Synopsysの人が書いている。その他の章はRaspberry Pi財団の書いたブリティッシュイングリッシュだが、この部分だけ多分ネイティブじゃない人の書いたいい加減な英語である。誤植も多い。何日かかけて理解しようとしたがよく分からん。不毛なので一旦諦めた。</p>
+前回: <a href="xlib_playground2.html">Xlibで遊んでみる2</a>
+</p>
<p>
-RP2040には内部にもROMがあり、はバージョン情報や電源を投入した時の動作、その他便利な関数が書き込まれている。この関数の中に外部のフラッシュROMとSSIコントローラを設定するものも含まれているので、今回はこれを利用した。ただしこの方法だとフラッシュROMとの通信方式がStandard SPIのままなので少し遅いらしい。詳しくはデータシートの「2.3.8. Bootrom Contents」を参照。
+言語: C言語<br />
+ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
</p>
+
+<h2>画面サイズの変更</h2>
<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モードになる:
+画面サイズが変更された時に表示している四角形が画面の外側に出ないようにした。<code>XGetWindowAttributes()</code>で画面の情報を取得し、グローバル変数の<code>win_width</code>と<code>win_height</code>に幅と高さをそれぞれ代入して<code>next_tick()</code>で四角形の位置を画面に収まるようにしている:
</p>
-<pre><code>setup_xip:
- ldr r3, rom_base
+<pre><code>int win_width, win_height;
- 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
+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>
-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>
+ゲームのようなものを作るうえでメニュー画面とその推移が必要である。ここではグローバル変数<code>next_menu</code>に現在のメニューを保存することにした。それぞれのメニューはそれぞれ関数として記述し、他のメニューに推移する必要が生じたときに<code>next_menu</code>を変更するようにした:
+</p>
+<pre><code>enum next_menu {
+ START_MENU,
+ GAME_PLAY,
+ GAME_OVER,
+ QUIT,
+};
-<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
+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;
+ }
+ }
-flash_main:
- .word 0x10000000 + 0x100
-m0plus_vtor:
- .word 0xe0000000 + 0xed08
+ cleanup();
+ return 0;
+}
</code></pre>
-<p>なお以上のコードは<code>.boot2</code>という名前のセクションにしてある。
-</p>
+<p><code>main()</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)
-</code></pre>
+<h2>完成品</h2>
<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>
+<a href="https://git.mtkn.jp/xlib_playground/file/ex3/ex3.c.html">git</a>
+</p>
<p>
-この部分のセクション名は<code>.vectors</code>である。
+<video controls>
+<source src="videos/ex3.webm" type="video/webm">
+</video>
</p>
-<h3>GPIOの設定</h3>
+<h2>参考</h2>
+<ul>
+<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
+</ul>
<p>
-電源投入直後、RP2040の周辺機器はリセット状態になっている。まずは今回利用するGPIOのリセット状態を解除する必要がある。データシートの「2.14. Subsystem Resets」には以下のように書かれている:
+次の記事: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
</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.
+]]></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>
-</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>にする。
+
+<h2>FPSの固定</h2>
+<p>前のフレームからの経過時間を計測して<code>1.0/FPS</code>を越えるまで待機させる。このときに<code>nanosleep()</code>を使うとなぜか上手くいかなかった。ナノ秒単位で処理できそうな名前なのに使えない。多分OSのコンテクストスイッチがどうとかいう話やと思う。知らんけど。組み込みとかで使うんかな?
</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のリセットを解除するのは以下のコード:
+とりあえず<code>while</code>ループの中でひたすら時刻を読んでいる。リソースの無駄遣いではないのだろうか:
</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>#defin FPS 60
-/* ... */
+int
+main(void)
+{
+ long t0, t1, dt;
+ int fps_count;
-atomic_clr:
- .word 0x00003000
-resets_base:
- .word 0x4000c000
-</code></pre>
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ t0 = ts.tv_nsec;
-<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>
+ 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>
-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
-
-/* ... */
+時刻は<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>
+FPSの計測の部分は、フレーム毎に<code>fps_count</code>を1ずつ増やし、ナノ秒が繰り上がった時点での<code>fps_count</code>を表示している。</p>
+<p>
+あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。</p>
-io_bank0_base:
- .word 0x40014000
+<h2>キーボード入力の処理</h2>
+<p>キーボードからの入力を受け取る:</p>
+<pre><code>XSelectInput(display, window,
+ ExposureMask|KeyPressMask|KeyReleaseMask);
</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>ここではキーボードのキーを押した時と離した時に<code>XEvent</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
-
-/* ... */
+<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,
+};
-sio_base:
- .word 0xd0000000
+int key_state[Num_Key];
</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>
-</blockquote>
<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
- ldr r4, sio_base
- mov r5, r0 // r0 = 1 << 25
-loop:
- str r5, [r4, #0x1c] // SIO: GPIO_OUT_XOR
- bl delay
- b loop
+入力の処理は<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;
-delay:
- mov r0, #1
- lsl r0, r0, #20
-delay_loop:
- sub r0, r0, #1
- bne delay_loop
- bx lr
+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;
+ }
+ }
-sio_base:
- .word 0xd0000000
+ 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>なお以上のコードは<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
-}
+入力によって変更された速度は、<code>main()</code>関数内で次の座標を計算するために使用される:
+</p>
+<pre><code>float px = 200, py = 200;
+float vx = 0, vy = 0;
+int width = 40, height = 40;
-SECTIONS
+int
+main(void)
{
- .boot2 : {
- *(.boot2)
- . = 0x100;
- } > FLASH
+ /* ... */
+ 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;
- .text : {
- *(.vectors)
- *(.text)
- } > FLASH
+ 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>Makefile</h2>
-<p>
-以上のソースコードは以下のように配置している:
+<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>
-<pre><code>rp2040
-├── ex1
-│ ├── Makefile
-│ ├── boot2.s
-│ ├── main.s
-│ └── memmap.ld
-└── tools
- ├── Makefile
- ├── bin2uf2.c
- └── bincrc.c
-</code></pre>
-<p>
-toolsディレクトリのMakefileは同じディレクトリのソースファイルを<code>$(CC)</code>でコンパイルするだけのものである(個人的な趣味で<code>tcc</code>を使っている)。ex1ディレクトリのMakefileは以下の通り:
+]]></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>
-<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
+<h2>初期設定</h2>
+<p>ディスプレイを開き、ウィンドウを作成する。変数はとりあえずグローバルに宣言することにした。<code>main</code>関数はできるだけ小さくして実際の処理はそれぞれの関数にさせてみる:</p>
+<pre><code>
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
-all: tools led.uf2
+/* macros */
+#define INIT_WIDTH 800
+#define INIT_HEIGHT 600
-clean:
- rm -f *.o *.elf *.uf2 *.bin
- cd ../tools && make clean
+/* variables */
+Display *display;
+Window window;
+unsigned int win_width = INIT_WIDTH, win_height = INIT_HEIGHT;
+GC gc;
+Atom wm_delete_window;
-.s.o:
- $(AS) $(ASFLAGS) -o $@ $<
+void
+setup(void)
+{
+ // Open a display.
+ if ((display = XOpenDisplay(NULL)) == NULL){
+ fprintf(stderr, "ERROR: could not open display\n");
+ exit(1);
+ }
+ // 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");
-led.elf: boot2.o main.o memmap.ld
- $(LD) $(LDFLAGS) -o $@ -T memmap.ld boot2.o main.o
+ // Setup a graphical context.
+ gc = XCreateGC(display, window, 0, NULL);
+ XSetForeground(display, gc, 0x0000FF);
-led.bin: led.elf
- $(OBJCOPY) -O binary led.elf $@
+ // Kill the application when the window is destroyed.
+ wm_delete_window = XInternAtom(display,
+ "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(display, window, &wm_delete_window, 1);
-led.uf2: led.bin
- $(BINCRC) led.bin led_crc.bin
- $(BIN2UF2) led_crc.bin $@
+ // Setup which input to process.
+ XSelectInput(display, window,
+ ExposureMask|KeyPressMask|KeyReleaseMask);
-flash: all
- mount /dev/disk/by-label/RPI-RP2 /mnt
- cp led.uf2 /mnt
+ // Actually draw the window.
+ XMapWindow(display, window);
+}
-tools:
- cd ../tools && make
+void
+clean_up(void)
+{
+ XCloseDisplay(display);
+}
</code></pre>
-<p>
-RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し、ex1ディレクトリで</p>
-<pre><code>$ make
-# make flash
+<p>適当な四角形のものを表示し、その位置を時間の関数として動かしてみる。</p>
+<pre><code>#include <time.h>
+#include <math.h>
+
+int
+main(void)
+{
+ 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
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10 * 1000 * 1000;
+ nanosleep(&ts, NULL);
+ }
+
+ cleanup();
+ return 0;
+}
</code></pre>
-<p>
-とすればプログラムがRP2040のボードに書き込まれて実行が開始される。</p>
-<h2>最後に</h2>
-<p>
-光あれ。
-</p>
+<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>
-[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>
+<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>使用しているハードウェア、ソフトウェア</title>
<link>https://www.mtkn.jp/computer/what-i-use.html</link>
<guid>https://www.mtkn.jp/computer/what-i-use.html</guid>
-<pubDate>Tue, 2 May 2023 00:00:00 +0900</pubDate>
+<pubDate>Mon, 15 May 2023 00:00:00 +0900</pubDate>
<description><![CDATA[<h1>使用しているハードウェア、ソフトウェア</h1>
<time>2023-05-02</time>更新<br>
<time>2021-12-13</time>作成
@@ -914,6 +1031,7 @@ RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し
<dd>もうちょっとましなのないんかな。UIころころ変わるし、重いし、嫌い。JavaScriptがないと困るので仕方なく使っているが...</dd>
<dt>Mail Cdtent: neomutt</dt>
+<dd></dd>
</dl>
<h2>デスクトップ</h2>
@@ -930,6 +1048,7 @@ RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し
<dt>OS: OpenBSD</dt>
<dd>いい</dd>
<dt>ソフト: ノートパソコンと同じ</dt>
+<dd></dd>
</dl>
<h2>家のサーバー</h2>
@@ -940,6 +1059,7 @@ RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し
<dt>ハードウェア: Dell D520</dt>
<dd>おじいちゃんの家にころがってたのをもらってきた。</dd>
<dt>OS: OpenBSD</dt>
+<dd></dd>
</dl>
<h2>ウェブサーバー</h2>
@@ -974,999 +1094,882 @@ RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し
]]></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>Tue, 25 Apr 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる6</h1>
-<time>2023-01-25</time>
-
-<p>
-前回: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
-</p>
-<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
-</p>
-
-<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>
-
-<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 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>
-
-<p>ユーザーからの入力を受けとり、プレイーヤの加速度等を変更。<code>A</code>、<code>D</code>でそれぞれ左右に加速し、地面に接しているときに<code>space</code>キーでジャンプさせる:
-</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>
+<title>RP2040 SDKなし2 Clock, UART</title>
+<link>https://www.mtkn.jp/computer/rp2040_2.html</link>
+<guid>https://www.mtkn.jp/computer/rp2040_2.html</guid>
+<pubDate>Thu, 11 May 2023 00:00:00 +0900</pubDate>
+<description><![CDATA[<h1>RP2040 SDKなし2 Clock, UART</h1>
+<time>2023-05-10</time>
<p>
-<video controls>
-<source src="videos/ex6.webm" type="video/webm">
-</video>
+前回: <a href="rp2040_1.html">RP2040 SDKなしでLチカ</a><br>
+ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex2
</p>
-<h2>参考</h2>
+<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>Tue, 25 Apr 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる5</h1>
-<time>2023-01-03</time>
-
-<p>
-前回: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
-</p>
-<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
-</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
-};
-
-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>
-衝突後は前回と同じく弾性衝突として処理した。四角形とは違い、衝突方向の場合分けが不要なので楽である。
-</p>
-<pre><code>
-void
-circle_handle_collision_mm(struct circle *c1, struct circle *c2)
-{
- if (!circle_test_collision(c1, c2))
- return;
-
- 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;
-
- 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;
-}
-
-void
-circle_handle_collision_elastic(struct circle *c1, struct circle *c2)
-{
- if(!circle_test_collision(c1, c2))
- return;
-
- 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;
+<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>
- float m1 = c1->m;
- float m2 = c2->m;
+<h2>Clock</h2>
- float col_1v = c1->vx * col_px + c1->vy * col_py;
- float col_2v = c2->vx * col_px + c2->vy * col_py;
+<h3>リング発振回路</h3>
+<p>RP2040にはリング発振回路というのが内蔵されている。これは自分の出力を反転させようとするもので、不安定だが高速で消費電力の少ないクロックとして用いられる。RP2040は電源を入れると、このリング発振回路を動作用のクロックとして用いている。この発振回路の周波数は、チップの製造過程での誤差、動作時の電圧、動作温度によって変動するので、正確な周波数が必要な用途には向かない。</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;
+<h3>水晶発振子</h3>
+<p>秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、より正確である。</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);
+<h3>PLL</h3>
+<p>水晶振動子を入力として、周波数を数倍にしたものを出力するもの。電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」によると、入力周波数を<code>FREF</code>としたときの出力周波数は<code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。これらの変数はそれぞれ設定用のレジスタに値を保存することで変更できる。</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;
+<h2>UART</h2>
- circle_handle_collision_mm(c1, c2);
-}
-</code></pre>
+<h2>リング発振回路でUARTは動くんかな?</h2>
+<p>UARTの通信には正確なクロックが必要である。その為上では<code>clk_peri</code>として水晶発振子とPLLを用いた。ところがpico-examplesのhello_uartでは<code>main()</code>関数で水晶発振子を設定していない。そこでリング発振回路を用いてみたのだが、どうもうまく通信できない。出力されている正確な周波数も分からないのであきらめることにした。オシロスコープなんていうものは持っていない。</p>
-<h2>完成品</h2>
-<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex5/ex5.c.html">git</a>
-</p>
+<h3>pico-sdk</h3>
<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>
-</ul>
+ところがどうも調べているとSDKを使った場合、デフォルトではクロック周波数は125MHzになっているらしい。どうやら水晶発振子もPLLも<code>main()</code>が呼ばれる前に設定されているようである。</p>
<p>
-次の記事: <a href="xlib_playground6.html">Xlibで遊んでみる6</a>
-</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>Tue, 25 Apr 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる4</h1>
-<time>2023-01-02</time>
-
+pico-examplesのサンプルプログラムはビルドすると自動で逆アセンブリしたファイルを出力してくれる。これを見ると、最初の256バイトは前回説明したboot2のコードで、その後ろにベクターテーブルが続く。ベクターテーブルの最初は初期スタックポインタで、<code>0x20042000</code>になっている。次はエントリーポイントで、<code>0x100001f7</code>である:</p>
+<pre><code>10000100 <__VECTOR_TABLE>:
+10000100: 20042000 .word 0x20042000
+10000104: 100001f7 .word 0x100001f7
+</code></pre>
<p>
-前回: <a href="xlib_playground3.html">Xlibで遊んでみる3</a>
+Thumbモードなので実際のエントリーポイントは<code>1</code>引いた、<code>0x100001f6</code>である。この場所ではまず自分のCPUIDを調べて、<code>1</code>であれば待機状態に移行する。RP2040はデュアルコアである。起動直後はCPUIDが<code>0</code>のコアだけで処理をして、CPUIDが<code>1</code>のコアはプログラマが必要に応じて起動することになっている。このためCPUIDが<code>1</code>のコアは起動してすぐに待機状態に入ることがデータシートに書かれている。しかしこの処理はユーザーの書いたプログラムじゃなくて内蔵ROMにある起動用プログラムが担当するみたいに書かれてるんやけど、なんでSDKではユーザープログラムの一部として組み込んでるんかな?
</p>
-<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+<pre><code>100001f6 <_reset_handler>:
+100001f6: 481d ldr r0, [pc, #116] ; (1000026c <hold_non_core0_in_bootrom+0xe>)
+100001f8: 6800 ldr r0, [r0, #0]
+100001fa: 2800 cmp r0, #0
+100001fc: d12f bne.n 1000025e <hold_non_core0_in_bootrom>
+</code></pre>
+<p>上のコードの最初の<code>ldr</code>は、<code>0xd0000000</code>(M0PLUS: CPUIDレジスタ)をロードしている。最後の飛び先<code>0x1000025e</code>はCPUIDが<code>1</code>のCPUを待機させる処理である:</p>
+<pre><code>1000025e <hold_non_core0_in_bootrom>:
+1000025e: 4809 ldr r0, [pc, #36] ; (10000284 <hold_non_core0_in_bootrom+0x26>)
+10000260: f001 fb9c bl 1000199c <rom_func_lookup>
+10000264: 4700 bx r0
+10000266: 0000 .short 0x0000
+/* ... */
+10000284: 00005657 .word 0x00005657
+</code></pre>
+<p>内蔵フラッシュに書きこまれた関数を呼びだしている。呼びだしに使うコードは<code>0x00005657</code>(<code>'W' | 'V' << 8</code>)である。データシートを見ると、この関数は<code>_wait_for_vector()</code>という名前で、CPUIDが1のCPUを寝かしつけるのに使われると書いている。この部分のソースコードをpico-sdkで探すと<code>pico-sdk/src/rp2_common/pico_standard_link/crt0.S</code>というのが見付かった:</p>
+<pre><code>$ find pico-sdk/src -type f | xargs grep -l _reset_handler
+pico-sdk/src/rp2_common/pico_standard_link/crt0.S
+</code></pre>
+<p>このファイルによると:
</p>
+<pre><code> // Only core 0 should run the C runtime startup code; core 1 is normally
+ // sleeping in the bootrom at this point but check to be sure
+</code></pre>
+<p>だそうである。やっぱり無駄やん。内蔵フラッシュのプログラムにバグがあってもこのコードのせいで見付かりにくくなってない?知らんけど。</p>
-<h2>衝突判定とその処理</h2>
-<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
-};
+<p>続いて<code>.data</code>領域と<code>.bss</code>領域のコピー、初期化のようである。多分OSの本かなんかで習ったメモリマップの話:</p>
+<pre><code>100001fe: a40d add r4, pc, #52 ; (adr r4, 10000234 <data_cpy_table>)
+10000200: cc0e ldmia r4!, {r1, r2, r3}
+10000202: 2900 cmp r1, #0
+10000204: d002 beq.n 1000020c <_reset_handler+0x16>
+10000206: f000 f812 bl 1000022e <data_cpy>
+1000020a: e7f9 b.n 10000200 <_reset_handler+0xa>
+1000020c: 4918 ldr r1, [pc, #96] ; (10000270 <hold_non_core0_in_bootrom+0x12>)
+1000020e: 4a19 ldr r2, [pc, #100] ; (10000274 <hold_non_core0_in_bootrom+0x16>)
+10000210: 2000 movs r0, #0
+10000212: e000 b.n 10000216 <bss_fill_test>
-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>
+10000214 <bss_fill_loop>:
+10000214: c101 stmia r1!, {r0}
-<p>
-衝突後の処理は多少めんどくさかった。衝突した時は既にめりこんでいるので、まずはそれぞれをめりこんだ距離の半分ずつずらして衝突を解消するようにした。この際、x軸方向にぶつかったのか、y軸方向にぶつかったのかで、それぞれの軸方向にひっぺがすようにしている。二つの四角形の各軸に関するめりこんだ距離<code>lapx</code>、<code>lapy</code>と各軸に関する相対速度<code>rel_vx</code>、<code>rel_vy</code>の比を比べればどちらの軸方向にぶつかったかが分かるはずである、多分 :
-</p>
-<pre><code>void
-handle_collision_mm(struct square *s1, struct square *s2)
-{
- if (!test_collision(s1, s2))
- return;
+10000216 <bss_fill_test>:
+10000216: 4291 cmp r1, r2
+10000218: d1fc bne.n 10000214 <bss_fill_loop>
+</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);
- float rel_vx = max(s1->vx - s2->vx, s2->vx - s1->vx);
- float rel_vy = max(s1->vy - s2->vy, s2->vy - s1->vy);
+<p>最後にいろいろ呼びだす:</p>
+<pre><code>1000021a <platform_entry>:
+1000021a: 4917 ldr r1, [pc, #92] ; (10000278 <hold_non_core0_in_bootrom+0x1a>)
+1000021c: 4788 blx r1
+1000021e: 4917 ldr r1, [pc, #92] ; (1000027c <hold_non_core0_in_bootrom+0x1e>)
+10000220: 4788 blx r1
+10000222: 4917 ldr r1, [pc, #92] ; (10000280 <hold_non_core0_in_bootrom+0x22>)
+10000224: 4788 blx r1
+10000226: be00 bkpt 0x0000
+10000228: e7fd b.n 10000226 <platform_entry+0xc>
+/* ... */
+10000278: 10001819 .word 0x10001819
+1000027c: 100002dd .word 0x100002dd
+10000280: 10001909 .word 0x10001909
+</code></pre>
+<p>一つめの<code>blx</code>は<code>0x10001818</code>(<code>runtime_init</code>)を、二つめは<code>0x100002dc</code>(<code>main</code>)を、最後のは<code>0x10001908</code>(<code>exit</code>)を、それぞれ呼んでいる。この<code>runtime_init</code>はアセンブリでは分かりにくいのでソースコードを探してみると、以下のものが見付かった:</p>
+<pre><code>$ find pico-sdk/src -type f | xargs grep -l runtime_init
+pico-sdk/src/rp2_common/pico_runtime/runtime.c
+pico-sdk/src/rp2_common/pico_standard_link/crt0.S
+pico-sdk/src/common/pico_sync/include/pico/mutex.h
+</code></pre>
+<p>最後の<code>mutex.h</code>は関係なさそう。二つめの<code>crt0.S</code>は呼びだしてるだけ。一つめの<code>runtime.c</code>が多分探しているものである。これを見るとまず各種周辺機器を一度リセットし、リセット状態を解除している。使わんやつも初期化してない?その後<code>clocks_init()</code>を呼んでいる。この関数は<code>pico-sdk/src/rp2_common/hardware_clocks/clocks.c</code>で定義されている。これを見ると、<code>xosc_init()</code>を呼んで水晶発振子を初期化した後、<code>clk_peri</code>を125MHzに設定している:</p>
+<pre><code> clock_configure(clk_peri,
+ 0,
+ CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
+ 125 * MHZ,
+ 125 * MHZ);
+</code></pre>
+<p>やっぱり水晶発振子じゃないとあかんのかな。</p>
- 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;
- }
- }
-}
-</pre></code>
-<p>
-衝突は弾性衝突として、衝突したそれぞれの四角形の速度を更新した。質量は四角形の面積として計算している。衝突後の速度はエネルギー保存則と運動量保存則から導いたのでしんどかった。
-</p>
-<pre><code>void
-handle_collision_elastic(struct square *s1, struct square *s2)
-{
- if(!test_collision(s1, s2))
- return;
+<h2>CMake</h2>
+<p>上ではビルドしたバイナリを逆アッセンブルして読んだ。わざわざこんなことをしなくてもMakefile読めばなにがどうなって最終生成物に辿りつくのか分かればいいのだが、そうもいかない。このSDKとpico-examplesにはビルドシステムとしてCMakeなるものが使われている。これがどうも複雑でよく分からない。勉強する気にもならん。上で見た<code>crt0.S</code>や<code>runtime.c</code>といったファイルも<code>hello_uart</code>で本当に使われているものなのかもよく分からない。こんな煩雑なものは本当に必要なのかな。無駄に複雑にしてるだけとちゃうんかな。特に僕は勉強用に使ってるので、ソースコードの依存関係をもっと分かりやすくしてくれないと、内部でなにがどうなってるのか理解しにくい。何度か頑張って読もうとしたが、面白くないのでやめた。数百行のファイルをあっちからこっちから<code>include</code>してるし、大文字ばかりの変数だらけで目が痛い。こんなものを扱えるというのはえらいええ頭してはるんやね<sup>†</sup>。</p>
- 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);
+<h2>参考</h2>
+<ul>
+<li>
+<a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a>
+</li>
+<li>
+<a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a>
+</li>
+<li>
+<a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a>
+</li>
+<li>
+<a href="https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%82%B7%E3%83%AC%E3%83%BC%E3%82%BF">リング・オシレータ.Wikipedia</a>
+</li>
+<li>
+<a href="https://www5.epsondevice.com/ja/information/technical_info/osc.html">水晶発振器とは? 原理と仕組み、水晶振動子との違い、選び方のポイントを解説.エプソン水晶デバイス</a>
+</li>
+</ul>
- 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;
- }
+<p><sup>†</sup>僕は和歌山の人間である。</p>
- handle_collision_mm(s1, s2);
-}
-</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>
+<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>
+<p>
+ということでSDKなしで開発してみる。とりあえず定番のLチカをば。</p>
+<p>
+ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>
</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>
+<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>
+</ul>
-<h2>完成品</h2>
+<h2>Boot Process</h2>
<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex4/ex4.c.html">git</a>
+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>
-<video controls>
-<source src="videos/ex4.webm" type="video/webm">
-</video>
+以上のことから、プログラムを実行するためには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>参考</h2>
-<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-</ul>
+<h2>CRC(巡回冗長検査)</h2>
<p>
-次の記事: <a href="xlib_playground5.html">Xlibで遊んでみる5</a>
+入力のデータをごにょごにょしてある値を出力する。</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>
-]]></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>Tue, 25 Apr 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる3</h1>
-<time>2023-01-02</time>
-
+</blockquote>
<p>
-前回: <a href="xlib_playground2.html">Xlibで遊んでみる2</a>
+らしい。
</p>
<p>
-言語: C言語<br />
-ソースコード: <a href="https://git.mtkn.jp/xlib_playground">git</a>
+入力のビットを一列に並べて、除数で「割り算」していく。この「割り算」が多項式の除算に似ているので、この除数をCRC多項式というらしい。ただし多項式の除算と違い、引き算するところをXORする。CRC32の場合、除数は33ビットである。33ビットで割ると32ビットの余りが残る。この余りがCRC32のチェックサムである。除数は色々あるようだが、標準的なものがWikipedia<sup>[5]</sup>に列挙されている。除数<code>1011</code>を使ったCRC3の計算の手順は以下の通り:
</p>
-
-<h2>画面サイズの変更</h2>
+<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>
-画面サイズが変更された時に表示している四角形が画面の外側に出ないようにした。<code>XGetWindowAttributes()</code>で画面の情報を取得し、グローバル変数の<code>win_width</code>と<code>win_height</code>に幅と高さをそれぞれ代入して<code>next_tick()</code>で四角形の位置を画面に収まるようにしている:
+普通の割り算と基本は同じであるが、引き算の部分だけXORになっている。</p>
+<p>
+以上の計算をプログラムの先頭252バイトに対して、33ビットの除数を用いて行う。データの並べ方は、上の例において左側を先頭に、フラッシュROM上の0番地から、各バイトは最上位ビットから順に並べる。入力のデータは253バイト目から256バイト目に<code>0</code>をひっつけて計算する。これは多分予め長さが分からないデータでも計算できるようにしたかったからかな。除数は<code>0x104c11db7</code>である(最上位ビットは常に1なのでデータシートでは省略されている)。</p>
+<p>
+入力データは1バイトづつ処理したいみたいである。多分通信等で使う都合である。この時XORは結合則が成り立つので1バイト処理した結果と次のバイトとをXORして次の処理の入力として利用することができる:
</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;
-}
+<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
+|......|
+111000110000000000000000000000000 先頭1バイト
+100000100110000010001110110110111 除数
+------------------------------------------------------------------------
+011000010110000010001110110110111
+ 100000100110000010001110110110111
+ -----------------------------------------------------------------------
+ 010000001010000110010011011011001
+ 100000100110000010001110110110111
+ ----------------------------------------------------------------------
+ 000000110010001110101000000000101
+|......|
+ 110010001110101000000000101000000 1バイト目の結果
+ |......|
+ 10000001 入力の2バイト目
+ ----------------------------------------------------------------
+ 010010011110101000000000101000000 1バイト目の結果と2バイト目のXOR
+ 100000100110000010001110110110111 除数
+ ----------------------------------------------------------------
+ 000100011011010010001111100110111
+ .
+ .
+ .
</code></pre>
-
-<h2>メニュー画面の実装</h2>
<p>
-ゲームのようなものを作るうえでメニュー画面とその推移が必要である。ここではグローバル変数<code>next_menu</code>に現在のメニューを保存することにした。それぞれのメニューはそれぞれ関数として記述し、他のメニューに推移する必要が生じたときに<code>next_menu</code>を変更するようにした:
+以上の操作は以下のようなアルゴリズムのループで実装できる。</p>
+<ul>
+<li>前回の結果と、入力データの次のバイトをXOR</li>
+<li>
+ <ul>
+ <li>先頭の1ビットが1の場合、除数とXORを取り左シフト</li>
+ <li>先頭の1ビットが0の場合、そのまま左シフト</li>
+ </ul>
+</li>
+</ul>
+<p>
+これを<code>for</code>ループで回す都合上、最初のバイトもXORを取る。上の例では最初は<code>0x0</code>とXORを取っているが、この値を<code>0x0</code>以外にすることもできる。そうした方がいろいろいいこともあるらしい。RP2040では<code>0xffffffff</code>を使う。更にこの工程を32ビットの<code>int</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));
-
- 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));
+<pre><code>111000111000000110000110111000111000001010010011111000111000000110010011 入力(適当)
- } 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;
- }
- }
-}
+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バイト目の結果
-int
-main(void)
+10000001 入力の2バイト目
+-------------------------------- XOR
+00000111111000101011111100010100
+先頭1ビットが0なので1ビットシフト
+-------------------------------- シフト
+00001111110001010111111000101000
+.
+.
+.
+</code></pre>
+<p>
+これを実装したのが以下のコード:</p>
+<pre><code>uint32_t
+crc32(uint8_t *idata, size_t len)
{
- 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;
+ uint32_t pol = 0x04C11DB7;
+ uint32_t c = 0xFFFFFFFF;
+ uint32_t b;
+
+ 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;
}
}
- cleanup();
- return 0;
+ return c;
}
</code></pre>
-<p><code>main()</code>関数がめっちゃすっきりした。</p>
-
-<h2>完成品</h2>
-<p>
-<a href="https://git.mtkn.jp/xlib_playground/file/ex3/ex3.c.html">git</a>
-</p>
<p>
-<video controls>
-<source src="videos/ex3.webm" type="video/webm">
-</video>
+<code>main()</code>関数では上の<code>crc32()</code>に、<code>idata</code>として入力となるバイナリデータの先頭を、<code>len</code>として<code>252</code>を渡してCRC32を計算させる。その後、出力先のファイルに入力元のデータをコピーしていき、253バイト目から256バイト目だけ、計算したCRC32に置き換える。入力元のこの場所にデータが書き込まれていないかどうかは確かめていない。
</p>
-<h2>参考</h2>
-<ul>
-<li><a href="https://tronche.com/gui/x/xlib/">The Xlib Manual(html conversion)</a></li>
-</ul>
+<h2>UF2(USB Flashing Format)</h2>
<p>
-次の記事: <a href="xlib_playground4.html">Xlibで遊んでみる4</a>
+Microsoftが開発したフラッシュ書き込み用のファイル形式らしい:
+<blockquote cite="https://github.com/microsoft/uf2">
+<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>
-]]></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>Tue, 25 Apr 2023 00:00:00 +0900</pubDate>
-<description><![CDATA[<h1>Xlibで遊んでみる2</h1>
-<time>2022-12-22</time>
+</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>
-<p>前回: <a href="xlib_playground1.html">Xlibで遊んでみる1</a></p>
-<p>言語はC言語である。ソースコードは<a href="https://git.mtkn.jp/xlib_playground">ここ</a>にある。
+<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>
-<h2>FPSの固定</h2>
-<p>前のフレームからの経過時間を計測して<code>1.0/FPS</code>を越えるまで待機させる。このときに<code>nanosleep()</code>を使うとなぜか上手くいかなかった。ナノ秒単位で処理できそうな名前なのに使えない。多分OSのコンテクストスイッチがどうとかいう話やと思う。知らんけど。組み込みとかで使うんかな?
-</p>
-<p>
-とりあえず<code>while</code>ループの中でひたすら時刻を読んでいる。リソースの無駄遣いではないのだろうか:
-</p>
-<pre><code>#defin FPS 60
+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;
+}
int
-main(void)
+main(int argc, char *argv[])
{
- long t0, t1, dt;
- int fps_count;
+ FILE *src = NULL, *dst = NULL;
+ size_t sdata = 476;
+ int retnum = 0;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- t0 = ts.tv_nsec;
+ 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;
- 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;
+ 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;
}
- // count fps.
- fps_count++;
- if (t1 < t0){
- printf("fps: %u\n", fps_count);
- fps_count = 0;
+ fwrite(data, 1, sdata, dst);
+ if (ferror(src)) {
+ fprintf(stderr, "Write error: %s.\n", argv[2]);
+ retnum = 1;
+ goto defer;
}
- clock_gettime(CLOCK_MONOTONIC, &ts);
- t0 = ts.tv_nsec;
+
+ 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>
-時刻は<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>
+CRC32のチェックサムが書き込まれたバイナリファイルを、このプログラムでUF2に変換し、生成されたファイルをUSBストレージとして接続したRP2040にコピーすればフラッシュROMに書き込まれる。
+</p>
+
+<h2>Flash Second Stage</h2>
<p>
-FPSの計測の部分は、フレーム毎に<code>fps_count</code>を1ずつ増やし、ナノ秒が繰り上がった時点での<code>fps_count</code>を表示している。</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>
<p>
-あまり正確な方法ではないように思うが、コンパクトにまとまったのではないだろうか。</p>
-
-<h2>キーボード入力の処理</h2>
-<p>キーボードからの入力を受け取る:</p>
-<pre><code>XSelectInput(display, window,
- ExposureMask|KeyPressMask|KeyReleaseMask);
-</code></pre>
-<p>ここではキーボードのキーを押した時と離した時に<code>XEvent</code>の通知を受け取るように設定した。
+しかしこの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>
-<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,
-};
+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
-int key_state[Num_Key];
+ 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>
-入力の処理は<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;
+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>
-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;
+<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
- 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;
-}
+flash_main:
+ .word 0x10000000 + 0x100
+m0plus_vtor:
+ .word 0xe0000000 + 0xed08
</code></pre>
+<p>なお以上のコードは<code>.boot2</code>という名前のセクションにしてある。
+</p>
+<h2>メインのコード(<code>main.s</code>)</h2>
+<h3>ベクターテーブル</h3>
<p>
-入力によって変更された速度は、<code>main()</code>関数内で次の座標を計算するために使用される:
+メインのコードの最初には上で説明したベクターテーブルを配置する。ここでは割り込みの処理は考えないので、初期スタックポインタとエントリーポイントだけである。初期スタックポインタは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>
-<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;
+<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>
+<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
- 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
- }
- /* ... */
-}
+/* ... */
+
+atomic_clr:
+ .word 0x00003000
+resets_base:
+ .word 0x4000c000
</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>
+<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>
+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
-<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>Tue, 25 Apr 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>
+io_bank0_base:
+ .word 0x40014000
+</code></pre>
-<h2>初期設定</h2>
-<p>ディスプレイを開き、ウィンドウを作成する。変数はとりあえずグローバルに宣言することにした。<code>main</code>関数はできるだけ小さくして実際の処理はそれぞれの関数にさせてみる:</p>
+<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>
-#include <stdio.h>
-#include <stdlib.h>
-#include <X11/Xlib.h>
+ // enable gpio output
+ ldr r3, sio_base
+ mov r0, #1
+ lsl r0, r0, #25 // gpio25
+ str r0, [r3, #0x24] // SIO: GPIO_OE
-/* 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;
+sio_base:
+ .word 0xd0000000
+</code></pre>
-void
-setup(void)
-{
- // Open a display.
- if ((display = XOpenDisplay(NULL)) == NULL){
- fprintf(stderr, "ERROR: could not open display\n");
- exit(1);
- }
- // 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");
+<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>
+</blockquote>
+<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>
- // Setup a graphical context.
- gc = XCreateGC(display, window, 0, NULL);
- XSetForeground(display, gc, 0x0000FF);
+<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
- // Kill the application when the window is destroyed.
- wm_delete_window = XInternAtom(display,
- "WM_DELETE_WINDOW", False);
- XSetWMProtocols(display, window, &wm_delete_window, 1);
+delay:
+ mov r0, #1
+ lsl r0, r0, #20
+delay_loop:
+ sub r0, r0, #1
+ bne delay_loop
+ bx lr
- // Setup which input to process.
- XSelectInput(display, window,
- ExposureMask|KeyPressMask|KeyReleaseMask);
+/* ... */
- // Actually draw the window.
- XMapWindow(display, window);
+sio_base:
+ .word 0xd0000000
+</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
}
-void
-clean_up(void)
+SECTIONS
{
- XCloseDisplay(display);
+ .boot2 : {
+ *(.boot2)
+ . = 0x100;
+ } > FLASH
+
+ .text : {
+ *(.vectors)
+ *(.text)
+ } > FLASH
}
</code></pre>
-<p>適当な四角形のものを表示し、その位置を時間の関数として動かしてみる。</p>
-<pre><code>#include <time.h>
-#include <math.h>
+<h2>Makefile</h2>
+<p>
+以上のソースコードは以下のように配置している:
+</p>
+<pre><code>rp2040
+├── ex1
+│ ├── Makefile
+│ ├── boot2.s
+│ ├── main.s
+│ └── memmap.ld
+└── tools
+ ├── Makefile
+ ├── bin2uf2.c
+ └── bincrc.c
+</code></pre>
+<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
-int
-main(void)
-{
- int px, py;
- int quit;
- struct timespec ts;
- XEvent event;
+MCPU = -mcpu=cortex-m0plus
+ASFLAGS = $(MCPU)
+CFLAGS = $(MCPU) -ffreestanding -nostartfiles -O0 -fpic -mthumb -c
+LDFLAGS = --no-relax -nostdlib
- setup();
- quit = 0;
+all: tools led.uf2
- 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
+clean:
+ rm -f *.o *.elf *.uf2 *.bin
+ cd ../tools && make clean
- ts.tv_sec = 0;
- ts.tv_nsec = 10 * 1000 * 1000;
- nanosleep(&ts, NULL);
- }
+.s.o:
+ $(AS) $(ASFLAGS) -o $@ $<
- cleanup();
- return 0;
-}
+led.elf: boot2.o main.o memmap.ld
+ $(LD) $(LDFLAGS) -o $@ -T memmap.ld boot2.o main.o
+
+led.bin: led.elf
+ $(OBJCOPY) -O binary led.elf $@
+
+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>
-<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>
+<p>
+RP2040のボードをUSBデバイスモードでLinuxのパソコンに接続し、ex1ディレクトリで</p>
+<pre><code>$ make
+# make flash
+</code></pre>
+<p>
+とすればプログラムがRP2040のボードに書き込まれて実行が開始される。</p>
+
+<h2>最後に</h2>
+<p>
+光あれ。
+</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>
+<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>
-
-<a href="xlib_playground2.html">次の記事</a>
]]></description>
</item>
<item>
diff --git a/pub/sitemap.xml b/pub/sitemap.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://www.mtkn.jp/</loc><lastmod>2023-12-28</lastmod></url>
+<url><loc>https://www.mtkn.jp/computer/xlib_playground6.html</loc><lastmod>2023-05-15</lastmod></url>
+<url><loc>https://www.mtkn.jp/computer/xlib_playground5.html</loc><lastmod>2023-05-15</lastmod></url>
+<url><loc>https://www.mtkn.jp/computer/xlib_playground4.html</loc><lastmod>2023-05-15</lastmod></url>
+<url><loc>https://www.mtkn.jp/computer/xlib_playground3.html</loc><lastmod>2023-05-15</lastmod></url>
+<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/what-i-use.html</loc><lastmod>2023-05-15</lastmod></url>
<url><loc>https://www.mtkn.jp/computer/rp2040_2.html</loc><lastmod>2023-05-11</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/computer/what-i-use.html</loc><lastmod>2023-05-02</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/xlib_playground6.html</loc><lastmod>2023-04-25</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/xlib_playground5.html</loc><lastmod>2023-04-25</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/xlib_playground4.html</loc><lastmod>2023-04-25</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/xlib_playground3.html</loc><lastmod>2023-04-25</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/xlib_playground2.html</loc><lastmod>2023-04-25</lastmod></url>
-<url><loc>https://www.mtkn.jp/computer/xlib_playground1.html</loc><lastmod>2023-04-25</lastmod></url>
<url><loc>https://www.mtkn.jp/computer/</loc><lastmod>2023-04-25</lastmod></url>
<url><loc>https://www.mtkn.jp/poetry/</loc><lastmod>2023-03-21</lastmod></url>
<url><loc>https://www.mtkn.jp/journal/posts/20230119.html</loc><lastmod>2023-01-19</lastmod></url>