デバッガー機能の実装
[YACASL2.git] / src / debugger.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 #include "debugger.h"
5 #include "hash.h"
6 #include "cmem.h"
7 #include "cerr.h"
8 #include "exec.h"
9
10 /**
11  * アドレスに対応するハッシュ値を返す
12  *
13  * @return ハッシュ値
14  *
15  * @param adr アドレス
16  */
17 unsigned adrhash(WORD adr);
18
19 /**
20  * @brief ブレークポイント数
21  */
22 static int bpscnt = 0;
23
24 /**
25  * @brief ブレークポイント表
26  */
27 static BPSTAB *bps[BPSTABSIZE];
28
29 /**
30  * @brief ブレークポイントのエラー
31  */
32 static CERR cerr_bps[] = {
33     { 101, "break point already defined" },
34     { 102, "break point table is full" },
35     { 103, "break point not found" },
36 };
37
38 /**
39  * @brief アドレスのハッシュ値を返す
40  *
41  * @return ハッシュ値
42  *
43  * @param adr アドレス
44  */
45 unsigned adrhash(WORD adr);
46
47 /**
48  * @brief ブレークポイントのエラーをエラーリストに追加する
49  *
50  * @return なし
51  */
52 void addcerrlist_bps();
53
54
55 unsigned adrhash(WORD adr)
56 {
57     HKEY *key[1];
58     unsigned h;
59     key[0] = malloc_chk(sizeof(HKEY), "adrhash.key");
60     key[0]->type = INT;
61     key[0]->val.i = adr;
62     h = hash(1, key, BPSTABSIZE);
63     FREE(key[0]);
64     return h;
65 }
66
67 void addcerrlist_bps()
68 {
69     addcerrlist(ARRAYSIZE(cerr_bps), cerr_bps);
70 }
71
72 bool getbps(WORD adr)
73 {
74     BPSTAB *p;
75
76     for(p = bps[adrhash(adr)]; p != NULL; p = p->next) {
77         if(p->adr == adr) {
78             return true;
79         }
80     }
81     return false;
82 }
83
84 bool addbps(WORD adr)
85 {
86     BPSTAB *p;
87     unsigned h;
88
89     /* 登録されたラベルを検索。すでに登録されている場合はエラー発生 */
90     if(getbps(adr) == true) {
91         setcerr(101, "");    /* breakpoint already defined */
92         return false;
93     }
94     /* メモリを確保 */
95     p = malloc_chk(sizeof(BPSTAB), "bps.next");
96     /* アドレスを設定 */
97     p->adr = adr;
98     /* ブレークポイント数を設定 */
99     bpscnt++;
100     /* ハッシュ表へ追加 */
101     p->next = bps[h = adrhash(adr)];
102     bps[h] = p;
103     return true;
104 }
105
106 bool delbps(WORD adr)
107 {
108     BPSTAB *p, *q;
109     unsigned h;
110
111     p = bps[h = adrhash(adr)];
112     if(p->adr == adr && p->next != NULL) {
113         bps[h] = p->next;
114         FREE(p);
115         return true;
116     } else {
117         for(; p != NULL; p = p->next) {
118             if(p->adr == adr) {
119                 q = p->next;
120                 p->next = q->next;
121                 FREE(p);
122                 return true;
123             }
124         }
125     }
126     return false;
127 }
128
129 void printbps()
130 {
131     int i;
132     BPSTAB *p;
133
134     fprintf(stdout, "breakpoints list\n");
135     for(i = 0; i < BPSTABSIZE; i++) {
136         for(p = bps[i]; p != NULL; p = p->next) {
137             fprintf(stdout, "#%04X\n", p->adr);
138         }
139     }
140 }
141
142 void freebps()
143 {
144     int i;
145     BPSTAB *p, *q;
146
147     for(i = 0; i < BPSTABSIZE; i++) {
148         for(p = bps[i]; p != NULL; p = q) {
149             q = p->next;
150             FREE(p);
151         }
152     }
153 }
154
155 DBARGS *dbargstok(const char *str)
156 {
157     DBARGS *args = malloc_chk(sizeof(DBARGS), "args");
158     char *p, *q, *r, *sepp;     /* pは文字列全体の先頭位置、qはトークンの先頭位置、rは文字の位置 */
159     int sepc = ' ';
160
161     args->argc = 0;
162     if(str == NULL) {
163         return args;
164     }
165     p = q = r = strdup_chk(str, "argstok.p");
166     do {
167         while(*q == ' ' || *q == '\t'){
168             q = ++r;
169         }
170         sepp = r + strcspn(r, " ");
171         sepc = *sepp;
172         *sepp = '\0';
173         args->argv[(++args->argc)-1] = strdup_chk(q, "args.argv[]");
174         q = r = sepp + 1;
175     } while(sepc == ' ');
176     FREE(p);
177     return args;
178 }
179
180 DBCMDLINE *dblinetok(const char *line)
181 {
182     char *tokens, *p, *sepp;
183     DBCMDLINE *dbcmdl = NULL;
184
185     if(*line == '\0') {
186         return NULL;
187     }
188     tokens = strdup_chk(line, "tokens");
189     p = tokens;
190     dbcmdl = malloc_chk(sizeof(DBCMDLINE), "dbcmdl");
191     /* コマンドと引数の取得 */
192     if(*p == '\n' || *p == '\0') {        /* コマンドがない場合は、終了 */
193         dbcmdl->dbcmd = '\0';
194     } else {
195         /* コマンドの取得 */
196         sepp = p + strcspn(p, " \t\n");
197         *sepp = '\0';
198         dbcmdl->dbcmd = strdup_chk(p, "dbcmdl.dbcmd");
199         p = sepp + 1;
200         /* コマンドと引数の間の空白をスキップ */
201         while(*p == ' ' || *p == '\t') {
202             p++;
203         }
204         /* 改行までの文字列を取得 */
205         if((sepp = p + strcspn(p, "\n")) > p) {
206             *sepp = '\0';
207             dbcmdl->dbargs = dbargstok(p);
208         } else {
209             dbcmdl->dbargs = malloc_chk(sizeof(DBARGS), "dbcmdl.dbargs");
210             dbcmdl->dbargs->argc = 0;
211         }
212     }
213     FREE(tokens);
214     return dbcmdl;
215 }
216
217 bool stracmp(char *str1, int str2c, char *str2v[])
218 {
219     int i;
220     for(i = 0; i < str2c; i++) {
221         if(strcmp(str1, str2v[i]) == 0) {
222             return true;
223         }
224     }
225     return false;
226 }
227
228 void debugger()
229 {
230     char *buf = malloc_chk(DBINSIZE + 1, "debugger.buf"), *cmd;
231     DBCMDLINE *dbcmdl =  malloc_chk(sizeof(DBCMDLINE), "dbcmdl");
232     DBARGS *arg;
233     for( ; ;) {
234         fprintf(stdout, "COMET II (Type ? for help) > ");
235         fgets(buf, DBINSIZE, stdin);
236         dbcmdl = dblinetok(buf);
237         cmd = dbcmdl->dbcmd;
238         arg = dbcmdl->dbargs;
239         if(*buf == '\n' || stracmp(cmd, 2, (char* []){"s", "step"})) {
240             break;
241         } else if(stracmp(cmd, 2, (char* []){"c", "continue"})) {
242             execmode.debugger = false;
243             break;
244         } else if(stracmp(cmd, 2, (char* []){"t", "trace"})) {
245             if(arg->argc > 0 && stracmp(arg->argv[0], 2, (char* []){"a", "auto"})) {
246                 execmode.logical = false;
247                 execmode.trace = true;
248             } else if(arg->argc > 0 && stracmp(arg->argv[0], 2, (char* []){"no", "noauto"})) {
249                 execmode.trace = false;
250             } else {
251                 fprintf(stdout, "#%04X: Register::::\n", sys->cpu->pr);
252                 dspregister();
253             }
254         } else if(stracmp(cmd, 2, (char* []){"T", "tracelogical"})) {
255             if(arg->argc > 0 && stracmp(arg->argv[0], 2, (char* []){"a", "auto"})) {
256                 execmode.logical = true;
257                 execmode.trace = true;
258             } else if(arg->argc > 0 && stracmp(arg->argv[0], 2, (char* []){"no", "noauto"})) {
259                 execmode.trace = false;
260             } else {
261                 fprintf(stdout, "#%04X: Register::::\n", sys->cpu->pr);
262                 dspregister();
263             }
264         } else if(stracmp(cmd, 2, (char* []){"d", "dump"})) {
265             if(arg->argc > 0 && stracmp(arg->argv[0], 2, (char* []){"a", "auto"})) {
266                 execmode.dump = true;
267             } else if(arg->argc > 0 && stracmp(arg->argv[0], 2, (char* []){"no", "noauto"})) {
268                 execmode.dump = false;
269             } else {
270                 dumpmemory();
271             }
272         } else if(*buf == '?') {
273             fprintf(stdout, "s (default) -- Step by step running your program until next interaction.\n");
274             fprintf(stdout, "c -- Continue running your program.\n");
275             fprintf(stdout, "t -- Display CPU register.\n");
276             fprintf(stdout, "d -- Display memory dump.\n");
277             break;
278         }
279     }
280 }