コグノスケ


link 未来から過去へ表示(*)
link 過去から未来へ表示

link もっと前
   2021年 10月 5日 ---> 2021年 9月 26日
link もっと後

2021年 10月 5日

久しぶりにファミコンの Might and Magic Book One をやってみたい

目次: Might and Magic ファミコン版 - まとめリンク

Nintendo Switch Online に加入すると、ファミコンやスーパーファミコンのソフトでも遊べるので、子供の時に挫折したゲーム(最近だとファミコンウォーズ、スーパーピクロス)をやっています。グラフィクスは近年のゲームと比べるまでもなくショボいですけど、今も名作は名作です。面白いですね。

子どもの時に挫折したゲームはいくつもありますが、ナンバーワンが Might and Magic Book One : the Secret of the Inner Sanctum です。この時代に流行ってた Wizardry みたいな 3D 風の迷路を歩いていく海外製 RPG です。難易度が異常に高くて有名で、小学生の私は Lv.2 すら拝むことなく諦めました。

この手の Switch には収録されていないソフトも遊びたいなあ?と思って、ニューファミコンを物色していたのですが、結構でかくて邪魔そうだし、中古の割に高いです。今でも人気なのか……侮ってましたね。

ですが、我々には PC とエミュレータがあるじゃないですか。幸いなことに、ファミコンソフトそのものはそんなに高くないので、ROM ダンパーで ROM を吸い出して、PC で遊ぶことにしました。


レトロダンパー

私はレトロダンパー(メーカーのサイト)を使っています。クライアントを起動して、認識ボタンを押し、吸い出すだけで OK なので便利です。

Might and Magic Book One 購入

早速、中古のカセットを購入しました。外観は割とズタボロというか、年季入った姿です。ま、動けば良いのさ。


ファミコン版 Might and Magic Book One のカセット

GAKKEN のロゴの通り、なぜか日本語ローカライズは学習研究社(学研)が行っています。今見ると不思議です。教科書作ってる学研が、なぜゲームの移植を……。


レトロダンパーにカセットを挿した状態

レトロダンパーさんで吸い出すときはこんな感じになります。吸い出した ROM をエミュレータに放り込んでみたところ、正常に動作しているようです。良きかな良きかな。

ゲームの感想

まずは普通に遊んでみました。今なら意外とクリアできるのでは?と思ったのも束の間、あっ、無理でした。調子乗ってすみませんでした。在りし日の絶望が蘇りました。

  • 攻撃がほぼ当たらない
  • 敵から逃げられない
  • Lv2 が果てしなく遠い
  • 罠解除役(盗賊)が宝箱の罠に掛かる
  • パーティーがすぐ全滅、タイトルに戻される

最初から難しすぎます。基本的には 1バトルごとに休憩+セーブって感じです。攻撃が当たらないのも辛く、一方的にボコられて死んでしまいます。死んだら復活させるお金がないのでリセットです。

さらに Wikipedia を見てびっくりしたのは、次の一文です。
「ファミコン版は(中略)やや簡単に調整された部分が多い」
えっ?これで?嘘だろ……??オリジナル版はどれだけ鬼畜難易度なの?

息抜きに YouTube で攻略動画を見ていると、ラストダンジョン(イドの迷宮、アストラル世界)の音楽がとてもカッコ良いですね。何とか辿り着きたいですが、最初の町(ソーピガルの町)から脱出できていない身からすると、果てしなく遠いです。

編集者: すずき(更新: 2021年 11月 8日 23:39)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 10月 4日

Might and Magic ファミコン版 - まとめリンク

目次: Might and Magic ファミコン版 - まとめリンク

日記が増えすぎて、一覧が欲しくなってきたので作りました。

リンク集

Might and Magic の攻略、解析の参考になるサイトです。

編集者: すずき(更新: 2021年 11月 24日 01:18)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 30日

携帯の遍歴

日記を漁って携帯の遍歴を書き出してみました。日記を書く習慣がなかった頃の機種や時期は不明です。

J-PHONE, Vodafone
  • 不明(2000年くらい?): SHARP J-SH04 辺り?
  • 不明(2002年くらい?): TOSHIBA J-T06 辺り?(ストレートだった気がする)
NTT ドコモ、ガラケー
  • 2004年 8月: Fujitsu F506i
  • 2005年 6月: Fujitsu F506i マイク故障で交換
  • 2006年 ?月: NEC N902iS
  • 2008年 ?月: NEC N902iS のバッテリー交換
  • 2010年 7月: Panasonic P-03B
NTT ドコモ、スマホ
  • 2011年 7月: Sony Ericsson Xperia acro SO-02C
  • 2014年 3月: SHARP AQUOS PHONE ZETA SH-01F
SIM フリー、スマホ
  • 2016年 11月: ASUS Zenfone 3 Deluxe
  • 2021年 4月: Google Pixel 4a

(基本的には)長く使っていた機種は気に入っていた機種です。ガラケー時代はいずれも良い機種で、バッテリーが死ぬまで使ってました。最後の P-03B だけ 1年しか使っていませんが、不満があったわけではなく、知人に携帯を譲るため手放しました。たしか。

スマホ時代は国内メーカーの質は明らかに落ちました。SO-02C はソツなく良かったんですけど、ストレージが少なすぎで買い替え直前は容量不足で挙動不審でした。SH-01F は性能良いものの、電池がなくなるのが早く、本体が熱すぎでした。この機種で懲りて Android ハイエンド機を買わなくなりました。

今になって調べてみたところ、この 2機種はマシな部類だったようで、富士通 ARROWS のように「カイロ機能搭載」「電話ができない」「メールがこない」など、怨嗟にまみれたレビューが未だに残っている機種もあります。悲惨です。

日本だけ異常に iPhone 普及率が高い理由って、国内メーカーが 2010 年代初頭にやらかしたから……!?と思ってしまいました……。

メモ: 技術系の話は Facebook から転記しておくことにした。

編集者: すずき(更新: 2021年 10月 8日 16:56)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 28日

マルチコアのブート処理

メイン CPU からサブ CPU を起こすとき基本的には、

  • CPU の ID(RISC-V であれば CSR の mhartid)を見て、自身がメインかサブかを知る
  • サブ側は共有 RAM をポーリングなどで見張り、メイン CPU からの起動司令を受け取るまで待つ
  • メイン側は共有 RAM に値を書いてサブ CPU に起動司令を送る

RAM の初期値が不定であると仮定すると、サブ CPU が下手にポーリングすると、不定値によって条件が成立してしまい、メイン CPU からの起動司令がないのに勝手に起動してしまう事態に陥ります。

素朴な実装

先程書いた基本的な構造を素直に書くとこんなコードになるでしょう。

素朴なマルチコアのブート

/* メイン CPU は HARTID=8, サブ CPU は HARTID=0...3 とする */

#define HARTID_MAIN         8
#define HARTID_SUB_START    0
#define HARTID_SUB_END      3

#define HARTID_MAX          9

struct {
	int boot_wait;
	int boot_done;
} init_core[HARTID_MAX] = {};

int get_hartid(void)
{
	int i;
	__asm__ volatile("csrr %0, mhartid" : "=r"(i));
	return i;
}

/* メイン CPU が実行する */
void boot_main(void)
{
	for (int i = HARTID_SUB_START; i < HARTID_SUB_END; i++) {
		init_core[i].boot_wait = 1;
	}
}

/* サブ CPU が実行する */
void boot_sub(void)
{
	int hartid = get_hartid();

	while (!init_core[hartid].boot_wait) {
		/* busy loop */
	}
}

残念ながらこのコードは正常に動作しません。共有 RAM つまり init_core[hartid].boot_wait の値が起動直後から != 0 だったとき、boot_sub() は boot_main() からの起動司令を待つことなく起動してしまうからです。

不定値への対処

共有 RAM の不定値に対処する方法を考えます。基本的にはサブ CPU が変数を初期化(boot_wait = 0)してから待ちに入れば良いのですが、新たな問題が生じます。メイン CPU とサブ CPU の実行順序はどちらが先という保証はないため、

  • メイン CPU からの起動司令 boot_wait = 1
  • サブ CPU で変数を初期化 boot_wait = 0

以上の順で実行されるとメイン CPU 側の起動司令が消されてしまい、ハングアップする可能性があります。この問題の回避のため、変数を 1つ追加し、サブ CPU のブートが終わるまで、メイン CPU は繰り返し起動司令を送るように変更します。

  • 変数を 2つ用意する(boot_wait, boot_done)
  • メイン: boot_done = 0
  • メイン: サブから応答(boot_done != 0)があるまで、boot_wait = 1 を書き続ける
  • サブ: boot_wait = 0
  • サブ: boot_done = 0
  • サブ: boot_wait == 0 なら待つ
  • サブ: boot_done = 1

先程書いた基本的な構造を素直に書くとこんなコードになるでしょう。

不定値への対処を入れたコード

/* メイン CPU は HARTID=8, サブ CPU は HARTID=0...3 とする */

#define HARTID_MAIN         8
#define HARTID_SUB_START    0
#define HARTID_SUB_END      3

#define HARTID_MAX          9

struct {
	int boot_wait;
	int boot_done;
} init_core[HARTID_MAX] = {};

int get_hartid(void)
{
	int i;
	__asm__ volatile("csrr %0, mhartid" : "=r"(i));
	return i;
}

/* メイン CPU が実行する */
void boot_main(void)
{
	for (int i = HARTID_SUB_START; i < HARTID_SUB_END; i++) {
		init_core[i].boot_done = 0;
		while (!init_core[i].boot_done) {
			init_core[i].boot_wait = 1;
		}
	}
}

/* サブ CPU が実行する */
void boot_sub(void)
{
	int hartid = get_hartid();

	init_core[hartid].boot_wait = 0;
	init_core[hartid].boot_done = 0;

	while (!init_core[hartid].boot_wait) {
		/* busy loop */
	}
	init_core[hartid].boot_done = 1;
}

残念ながらこのコードも正常に動作しません。共有 RAM への値の反映が他の CPU に即座に見えること(アトミック性)を暗に期待しているからです。

アトミック性への対処

今日のマルチコアシステムでは、boot_wait = 0 としたときに、他の CPU にも即座に同じ値が見えているとは限りません。主な要因としては、

  • コンパイラによる並べ替え
  • CPU のパイプライン
  • CPU のデータキャッシュ、ライトバッファ

などがあります。通常の変数への代入、参照が他の CPU に即座に値が見えないことにより、おかしくなるパターンはいくつか考えられそうですが、ありがちなパターンとして、

  • サブ CPU 0 が起動した boot_done = 1
  • メイン CPU に boot_done = 1 が伝わらず、サブ CPU 1 の起動指令がいつまでも送られない

以上の順で実行されるとメイン CPU 側が起動司令を送らないまま、サブ CPU 側も何もできずハングアップする可能性があります。この問題の回避のため、通常の変数への代入、参照ではなく他の CPU にも値が見えるように初期化、代入(アトミックアクセスする)必要があります。

従来 C 言語でアトミックアクセスを行うためには、実装対象アーキテクチャの知識やアセンブラの記述を必要とするなど、やや困難が伴いました。ですが C11 でアトミックアクセス用の定義 stdatomic.h が追加されたことで、アトミックアクセスはかなり楽になりました。素敵ですね。

ひとまず速度を全く気にせず、全てのアクセスをアトミックアクセスに入れ替えると、こんなコードになるでしょう。

アトミック性への対処を入れたコード

/* メイン CPU は HARTID=8, サブ CPU は HARTID=0...3 とする */

#define HARTID_MAIN         8
#define HARTID_SUB_START    0
#define HARTID_SUB_END      3

#define HARTID_MAX          9

struct {
	atomic_int boot_wait;
	atomic_int boot_done;
} init_core[HARTID_MAX] = {};

int get_hartid(void)
{
	int i;
	__asm__ volatile("csrr %0, mhartid" : "=r"(i));
	return i;
}

/* メイン CPU が実行する */
void boot_main(void)
{
	for (int i = HARTID_SUB_START; i < HARTID_SUB_END; i++) {
		atomic_store(&init_core[i].boot_done, 0);
		while (!atomic_load(&init_core[i].boot_done)) {
			atomic_store(&init_core[i].boot_wait, 1);
		}
	}
}

/* サブ CPU が実行する */
void boot_sub(void)
{
	int hartid = get_hartid();

	atomic_store(&init_core[hartid].boot_wait, 0);
	atomic_store(&init_core[hartid].boot_done, 0);

	while (!atomic_load(&init_core[hartid].boot_wait)) {
		/* busy loop */
	}
	atomic_store(&init_core[hartid].boot_done, 1);
}

C11 のアトミックアクセスは何も指定しない場合、一番制限の強い(= 確実に他の CPU に見えるものの、アクセス速度は遅い)memory_order_seq_cst アクセスになります。マルチコアのブートを行うにあたって、常に制限が強いアクセスは必要ありませんが、とりあえずこれで動くはず。

編集者: すずき(更新: 2021年 9月 30日 11:36)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2021年 9月 26日

xterm の 256色端末

まれに xterm の 256 色指定エスケープシーケンスに対応していない端末があって vim の表示が変な色になってしまいます。チェック用のスクリプトを作っておきました。単純に背景色を変更するエスケープシーケンスと、空白文字、色を元に戻すエスケープシーケンスを連打するだけです。

xterm 256色指定エスケープシーケンステスト用スクリプト

#!/bin/sh

ESC_ORG="\e[0m"

print_colors()
{
	for i in ${*};
	do
		printf " %3d\e[%dm  " ${i} ${i};
		echo -n ${ESC_ORG}
	done
	echo
}

print_xterm_colors()
{
	for i in ${*};
	do
		printf " %3d\e[48;5;%dm  " ${i} ${i};
		echo -n ${ESC_ORG}
	done
	echo
}

echo "System colors (ESC[Nm):"
print_colors `seq 40 47`

echo
echo "xterm 256 colors (ESC[48;5;Nm):"
for i in `seq 0 8 248`;
do
	j=`expr ${i} + 7`
	print_xterm_colors `seq ${i} ${j}`
done

実行するとこんな感じになります。


スクリプトの実行結果

対応していない端末だとこうなりますと言いたいところでしたが、対応していない端末が見当たりませんでした。前はあった気がするんだけどなあ……?

編集者: すずき(更新: 2021年 9月 29日 01:32)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
   2021年 10月 5日 ---> 2021年 9月 26日
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www2.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 11/26 11:42

カレンダー

<2021>
<<<10>>>
-----12
3456789
10111213141516
17181920212223
24252627282930
31------

最近のコメント 5件

  • link 20年06月19日
    すずき 「お役に立てて何よりです。これ、わかりにく...」
    (更新:09/27 17:05)
  • link 20年06月19日
    ちまき 「初めまして、こんばんは!\n音源は異なり...」
    (更新:09/27 03:16)
  • link 21年03月03日
    すずき 「お役に立てたようで幸いです。」
    (更新:06/17 17:24)
  • link 21年03月03日
    Shige 「とても参考になりました。\nGood!!...」
    (更新:06/17 13:28)
  • link 20年12月19日
    すずき 「なるほど、地元愛 No.1 は福岡なんで...」
    (更新:05/02 00:08)

最近の記事 3件

link もっとみる
  • link 19年07月21日
    すずき 「[RISC-V の命令] 目次: RISC-V - まとめリンク最...」
    (更新:11/26 11:42)
  • link 21年06月18日
    すずき 「[RISC-V まとめリンク] 目次: RISC-V - まとめリ...」
    (更新:11/24 15:26)
  • link 21年10月04日
    すずき 「[Might and Magic ファミコン版 - まとめリン] ...」
    (更新:11/24 01:18)

こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報