OSDN Git Service

[VM][FM7][DISPLAY] Not use CLR_HACK for MC6809 and delay using CLR to set busy flag...
[csp-qt/common_source_project-fm7.git] / doc / a_new_state_save_load_framework.ja.txt
1 *新ステートセーブフレームワークについて。
2                                     Jul 03, 2018 K.Ohta <whatisthis.sowhat@gmail.com>
3
4 1.背景 
5   武田俊也氏によるCommon Source Code Project (CSP)は、前に書いた文章でも書いてるように、
6   非常に設計の出来が良くて色々な人が参加しやすいように基本構造が出来ていて、しかも、GPLv2で
7   配布しているメリットが大きいわけですが、四五年ほど(もっとかも)ポーティングやFM-7シリーズを
8   移植するのにやっていると、もう少し改良したほうがよくないか?というものが度々出てきて、
9   ひんしゅくを買うかも知れないなどと震えながら、改良とか機能を「わがままに」追加してきました。
10   で、最近も色々作業をしていて、save_state()やload_state()で、Read/Writeのエレメント部分を
11   ベタ書きしていて、その後に後処理をやってるので、バグを仕込む温床になるよねー。と言う感じの
12   もにょりさを最近感じてました。
13
14   なので、
15
16   ・ステートのセーブロードは、エントリーを一個の関数で固めて行い、その関数が持ってる
17     エントリーリストに基づき、実際のデータをセーブもしくはロードできるようにする事で、
18     「セーブとロードで順番間違えたから、見た目正しくてもデータぶっ壊れてた」と言う悲
19     劇が出る余地を劇的に減らせる。
20   
21    ・それでも「凡ミス」はつきまとうものですから、CCITT-CRC32演算を、各データブロックごと
22     に計算して、一個の塊の末尾に結果を置くと同時に、簡単なヘッダや識別子で、このデータは
23     なんのステートを表現してるか見えやすくする…要は、デバッグにも改変にも有効な構造に変える。
24
25   ・他のホストマシンでステートロード/セーブしたときでも、ステートデータが使えるように
26    しておきたい。となると、エンディアンなどの問題を解決しておかないといけない。
27
28   と言う辺りを実際にやろうと考えて作業しております。
29  
30 2.実装系
31   2.1 クラス 
32   csp_state_utils:: : セーブやロードを実行する為のクラス。
33              一つのデバイスやVM、その他の「もの」(DISK:: とか)ごとに、このクラスを
34              使ってセーブ・ロードする。(APIは次で)
35
36    csp_state_data_saver:: 実際のデータを読んだり書いたりするクラス。通常はcsp_state_utils::
37                クラスの内部のみで使うが、呼び出す側で特殊な処理が必要な場合は、使用する。
38
39    又、実際の使用は、サポートルーチンやマクロを使って行うほうがよい。
40
41   2.1 扱うデータ型
42       (論理)数値 : bool , uintXX_t, intXX_t, float, double, long double
43            文字 (*1)  :  _TCHAR, _TCHAR *
44            ピクセル   : scrntype_t
45            固定長配列 : 1D_ARRAY, 2D_ARRAY, 3D_ARRAY
46            可変長配列 : VARARRAY_VAR
47            クラス     : FIFO*,  CUR_TIME_T
48            特殊    : DECL_STATE_ENTRY_CMT_RECORDING() : テープ録音(VM)絡みの処理に使う。
49                        
50
51        (*1) 単純に_TCHARの配列 として宣言した場合には、配列、
52                 DECL_STATE_ENTRY_STRING だと、0ターミネートの文字列として扱う
53                         
54    2.2 ファイル(今後分割するかも知れません)
55       src/statesub.h   : 使用者側が#include すべきヘッダ。必要な定義などは、このヘッダにまとめて読み込まれます。
56           src/state_data.h : 主に、csp_state_data_saver:: の内部定義が入ってる。
57           src/statesub.cpp : 処理実態の大半が入ってます。
58           src/common.cpp   : calc_crc32() など、補助ルーチンを新設しました。
59           src/vm/device.h  : エントリー定義に便利なメソッド定義をDEVICE:: にしてあります。
60
61    2.3 API(1)
62        csp_state_utils::csp_state_utils(int _version = 1, int device_id = 1,
63                                                                                 const _TCHAR *classname = NULL,
64                                                                                 CSP_Logger *p_logger = NULL)
65                        : コアクラスのコンストラクタ。Qt版では、QObjectの継承をして構成されてる。
66                                             (Qtの SIGNAL/SLOTモデルのメッセージングに対応するため)。
67         
68                 tempate <class T>
69                         void csp_state_utils::add_entry(const _TCHAR *__name, T*p,
70                                                                                 int _len = 1, int _num = -1,
71                                                                                 bool is_const = false, int stride = 0);
72
73                                                 : セーブ項目変数を登録する。変数はポインタでなければならない。
74                                                  次項で示す、DECL_STATE_ENTRY_foo系の宣言マクロを使うことを、
75                                                   *強く*推奨する。
76                                                   __name は、変数の名前、pは変数のあるポインタ(&var)、
77                                                   _lenは、変数の*要素数* (length / sizeof(T))。
78                                                   __numは、構造体の配列などで、メンバを記述する時に、配列内の
79                                                   「番号」を記述するための変数、is_constはロード時に内容変更さ
80                                                    れないフラグ。
81                                                   strideは、構造体全体の大きさ。sizeof(struct foo)。
82                                                   構造体の配列で、それぞれのメンバを定義するのに冗長さを抑え、
83                                                   メモリ消費や速度も向上させる。
84                                                   
85                 template <class T>
86                         void add_entry_vararray(const _TCHAR *__name, T **p, void *datalen,
87                                                                         bool assume_byte = false, int __num = -1,
88                                                                         int stride = 0);
89
90                                                  : 可変長配列(malloc/freeで管理される)を登録する。
91                                                   配列の先頭のポインタのポインタをpに、変数の要素数(Tな変数の個数)を
92                                                   格納する変数のポインタをdatalenに登録する。
93
94                 void add_entry_fifo(const _TCHAR *__name, FIFO **p,
95                                                         int _len = 1, int __num = -1, int stride = 0);
96
97                                                   : FIFO型のエントリ関数
98                                                   
99                 void add_entry_cur_time_t(const _TCHAR *__name, cur_time_t *p,
100                                           int _len = 1, int __num = -1, int stride = 0);
101
102                                                    : cur_time_t 型のエントリ関数。ポインタ渡しでなければならない。
103                                                    
104                 void add_entry_scrntype_t(const _TCHAR *__name, cur_time_t *p,
105                                           int _len = 1, int __num = -1, int stride = 0);
106
107                                                    : scrntype_t 型のエントリ関数。ポインタ渡しでなければならない。
108                                                    
109                 void add_entry_string(const _TCHAR *__name, _TCHAR *p,
110                                                          int _len = 1, int __num = -1, bool is_const = false);
111
112                                                    : 文字列型pを登録する。
113                                                    
114                 void add_entry_cmt_recording(const _TCHAR *__name,
115                                                                          FILEIO **__fio, bool* __flag, _TCHAR *__path); 
116
117                                                         : ロードセーブ時に、テープに録音する動作をしていた時に、
118                                                          リジューム動作が必要になるときのエントリ。
119                                                           __fio : テープイメージセーブ用に使われるFILEIO::クラスの
120                                                               ポインタ変数へのポインタ。
121                                                           __flag: trueで録音が残ってると示すbool方変数へのポインタ。
122                                                           __path: 読み書きしていた仮想テープのパス。
123
124                 bool save_state(FILEIO *__fio, uint32 *pcrc = NULL);
125                 bool load_state(FILEIO *__fio, uint32 *pcrc = NULL);
126                                                          : add_entry_FOO()で登録したエントリをload/saveする。
127                                                           __fio はsave_state(FILEIO *fio) のfio。
128                                                           *pcrc は、CRC変数のポインタ(なくてもよい。)
129                                                           
130                 bool csp_state_data_saver::pre_proc_saving(uint32_t *sumseed, bool *__stat);
131                 bool csp_state_data_saver::pre_proc_loading(uint32_t *sumseed, bool *__stat);
132                                                          : ステートセーブ/ロード前の事前処理をする。
133                                                           sumseed は、CRCを初期化したい変数のポインタ(通常、この変
134                                                            数の初期値は0xffffffffである)。
135                                                            __stat  は、成功か失敗かを知るときの判定変数へのポインタ。
136
137                 bool csp_state_data_saver::post_proc_saving(uint32_t *sumseed, bool *__stat);
138                                      : ステートセーブが終了したときの処理。
139                                                            sumseedにあるCRCを基に最終的な計算を行い、その値を追加する。
140                                                            
141                 bool csp_state_data_saver::post_proc_loading(uint32_t *sumseed, bool *__stat);
142                                                          : ステートロードのときの後処理。
143                                                           セーブ時に追加されたCRC値とsumseedから計算されたCRCを照合
144                                                           して、あっていればtrueを返す。
145                                                          
146         2.4 API(2) エントリ宣言マクロなど
147
148             FOO::decl_state()
149                         : そのデバイスなどのセーブステート項目を宣言する関数(スケルトンがDEVICE::にある)
150                                 
151         DEVICE::enter_decl_state(int version);
152                         : 各デバイスのdecl_state() 関数の冒頭に置くことで、
153                                  csp_state_utilsクラスをnew ・初期設定する。
154
155         DEVICE::leave_decl_state();
156                         : del_state()の末尾につけると良い。
157
158          
159         DECL_STATE_ENTRY_FOO(__name)
160                                     : FOOは基本的な変数型やFIFO*など : 一個の要素を宣言する。
161         DECL_STATE_ENTRY_FOO_MEMBER(__name, __num)
162                                     : FOOは基本的な変数型 : 一個の要素が__num目配列の要素のだと宣言する。
163         DECL_STATE_ENTRY_FOO_STRIDE(__name, __len, __stride)
164                                     : FOOは基本的な変数型 : 大きさ__strideでああり、__len回の配列となってる構造体内の
165                                                                     特定要素__nameを宣言する。
166
167             DECL_STATE_ENTRY_1D_ARRAY(__name, __lenvar)
168                                                         : 1次元配列(要素数__lenvar)を定義する。__lenvar = sizeof(ARRAY) / sizeof(__name)。
169                                                         
170             DECL_STATE_ENTRY_2D_ARRAY(__name, x, y)
171                                                         : 2次元配列(要素数[x][y])を定義する。
172
173             DECL_STATE_ENTRY_3D_ARRAY(__name, x, y, z)
174                                                         : 3次元配列(要素数[x][y][z])を定義する。
175
176                 DECL_STATE_ENTRY_STRING(__name, __len)
177                                     : 文字列型変数__name (最大長__len - 1)を定義する。
178
179                 DECL_STATE_ENTRY_VARARRAY_VAR(__name, __sizevar)
180                                                         : 可変長変数へのポインタ__nameが、__sizevarの値によってバイト数を
181                                                          規定される事を宣言する。
182                                                           uint8_t *__name = malloc(__sizevar);
183                                                           ...
184                                                           DECL_STATE_ENTRY_VARARRAY_VAR(__name, __sizevar);
185                                                           ...
186                                                           注意:__sizevarのセーブ・ロードは、__nameより前に行うこと。
187
188                 DECL_STATE_ENTRY_CMT_RECORDING(__fio, __flag, __path)
189                                                         :セーブでは:
190                                                            __fioをつかって、仮想テープを「録音」してることを__flagが示した場合に、
191                                                             書かれた仮想テープのデータをステートに書き込み、そのサイズも書き込む。
192                                                             __flagが録音していないと示した場合、(uint32_t)0をステートに書き込む。
193                                                          ロードでは:
194                                                               録音データがセーブされていた場合は、__pathが示すファイルにそのデータを
195                                                                   書き込み直す。
196
197
198 3.セーブデータ構造
199  ・各データは、ビッグエンディアンのバイナリ値で記録される(例外あり)。
200  ・文字列型データは、\0ターミネートして記録される。
201  ・実数型(float, double, long double)は、アスキー文字列に変換された形で記録される。
202     0.0 = "0.0" + '\0' など。
203   ・各デバイスのステート(チャンクの)書式は以下のようになっている:
204   
205           HEADER : ヘッダ ("CSP_SAVE" + '\0')
206        CLASSNAME : デバイス名(foo->this_device_name + '\0')
207         DEVICE_ID : デバイスID(foo->this_device_id      INT32)
208         STATE_VER : デバイス内のステートのヴァージョン  (INT32)
209          INTERNAL_VER : ステートフレーム側のヴァージョン
210 [バイナリデータ列] : DECL_STATE_ENTRY_fooで定義された実データの並び
211                 CRC32 : HEADER欄からバイナリデータ列末尾までのCRC32値をUINT32 Big Endianで記録する。
212                             与えるSEED値は0xffffffff.
213
214   ・VM::save_state()でセーブされるデータは、以下のような構造になっている:
215          HEADER   : "CSP_SAVE" + '\0'
216         CLASSNAME : (例) "CSP::VM_HEAD" + '\0'
217         DEVICE_ID : 通常0
218         STATE_VER : 各VMの(vm/foo/foo.cppの)STATE_VERSION
219          INTERNAL_VER : 通常0
220 [バイナリデータ列] : [必要に応じて。0バイトの場合もある]
221                 CRC32 :
222                         
223                         [DEVICE #2 (通常Event)のステートチャンク)]
224                         [DEVICE #3 のステートチャンク)]
225                                    ・
226                                            ・
227                                            ・
228                         [DEVICE #n(最後のデバイス) のステートチャンク)]
229             [EMU::側のフッター]
230
231     ※ DEVICE #1は通常DUMMY DEVICEであり、セーブ項目が、ない。
232 ---