/ [mix] / trunk / mixterm.w
To checkout: svn checkout http://svn.gnu.org.ua/sources/mix/trunk/mixterm.w
Puszcza

Contents of /trunk/mixterm.w

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations)
Wed Mar 30 16:43:11 2005 UTC (16 years, 10 months ago) by gray
File size: 16455 byte(s)
Initial revision

1 % This file is part of mix terminal emulator
2 % Copyright (C) 2000,2005 Sergey Poznyakoff
3 %
4 % This program is free software; you can redistribute it and/or modify
5 % it under the terms of the GNU General Public License as published by
6 % the Free Software Foundation; either version 2 of the License, or
7 % (at your option) any later version.
8 %
9 % This program is distributed in the hope that it will be useful,
10 % but WITHOUT ANY WARRANTY; without even the implied warranty of
11 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 % GNU General Public License for more details.
13 %
14 % You should have received a copy of the GNU General Public License
15 % along with this program; if not, write to the Free Software Foundation,
16 % Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 @*A MIX TERMINAL.
18 This provides a terminal shell for the MIX simulator. The shell supports following
19 commands:
20
21
22 \tabskip=1em \halign{%
23 \strut\sc#\hfil&\strut\sc#\hfil&\strut{\raggedright#}\cr
24 {\bf Synopsis} & {\bf Minimal abbreviation}& {\bf Short description}\cr
25 BREAK {\it addr} & B & Set breakpoint on address\cr
26 BREAK TEMP {\it addr} & BT & Set terminal breakpoint\cr
27 BREAK {\it addr} {\it number} & B & Set breakpoint for {\it number} crossings\cr
28 CLEAR BREAK {\it addr} & CB & Clear breakpoint\cr
29 LIST BREAK & LB & List breakpoints\cr
30 NEXT & N & Execute next\cr
31 STEP & S & Execute next and step into call\cr
32 ASGN {\it devnum} {\it unix-path} & A & Assign a file to \.{MIX} device\cr
33 GO & G & Run program\cr
34 DUMP & DU & Dump \.{MIX} machine state\cr
35 DUMP REGISTERS & DR & Dump \.{MIX} registers\cr
36 DUMP MEMORY [{\it addr} [{\it addr}]] & DM & Dump memory contents\cr
37 UNASM [{\it addr} [{\it addr}]] & U & Disassemble\cr
38 LIST IO [{\it devnum}] & LI & List {\sc I/O} devices\cr
39 CONT & CO & Continue after a breakpoint\cr
40 QUIT & Q & Quit the terminal\cr}
41
42 @ Definitions
43 @s CPPFunction int
44 @s token c
45 @s line normal
46 @d T_EOL '\n'
47 @d T_BREAK 256
48 @d T_TEMP 257
49 @d T_BT 258
50 @d T_CLEAR 259
51 @d T_CB 260
52 @d T_LIST 261
53 @d T_LB 262
54 @d T_NEXT 263
55 @d T_STEP 264
56 @d T_ASGN 265
57 @d T_GO 266
58 @d T_DUMP 267
59 @d T_REGISTERS 268
60 @d T_DR 269
61 @d T_MEMORY 270
62 @d T_DM 270
63 @d T_IO 271
64 @d T_LI 272
65 @d T_QUIT 273
66 @d T_CONT 274
67 @d T_UNASM 275
68 @d T_NUMBER 276
69 @d T_STRING 277
70
71 @d MAX_TOKENSIZE 256
72
73 @ Header files to include
74 @c
75 #ifdef HAVE_CONFIG_H
76 # include <config.h>
77 #endif
78 #include <stdlib.h>
79 #include <stdio.h>
80 #include <ctype.h>
81 #include <string.h>
82 #include <errno.h>
83 #include "mix.h"
84 #include "mixsim.h"
85 #ifdef WITH_READLINE
86 # include <readline/readline.h>
87 #endif
88
89 @
90 @c
91 struct command_word {
92 char *longname;
93 char *shortname;
94 int tok;
95 } cword[] = {@/
96 { "BREAK", "B", T_BREAK }, @/
97 { "TEMP", NULL, T_TEMP }, @/
98 { "BT", NULL, T_BT }, @/
99 { "CLEAR", NULL, T_CLEAR }, @/
100 { "CB", NULL, T_CB }, @/
101 { "LIST", NULL, T_LIST }, @/
102 { "LB", NULL, T_LB }, @/
103 { "NEXT", "N", T_NEXT }, @/
104 { "STEP", "S", T_STEP }, @/
105 { "ASGN", "A", T_ASGN }, @/
106 { "GO", "G", T_GO }, @/
107 { "DUMP", "DU", T_DUMP }, @/
108 { "REGISTERS", "REG", T_REGISTERS }, @/
109 { "DR", NULL, T_DR }, @/
110 { "MEMORY", "MEM", T_MEMORY }, @/
111 { "DM", NULL, T_DM }, @/
112 { "IO", NULL, T_IO }, @/
113 { "LI", NULL, T_LI }, @/
114 { "CONT", "CO", T_CONT }, @/
115 { "UNASM", "U", T_UNASM }, @/
116 { "QUIT", "Q", T_QUIT }, @/
117 { 0 } @/
118 };
119
120 @
121 @c
122 struct {
123 int type; /* Token type */
124 int number; /* Numeric value if appropriate */
125 char string[MAX_TOKENSIZE]; /* String value if appropriate */
126 } token;
127 char *buffer; /* Token buffer */
128 char *curp; /* Current position in the buffer */
129 char prompt[] = "MIX> "; /* Default terminal prompt */
130 int interactive; /* T if current session is interactive, F otherwise */
131
132 @* Readline support.
133 Wherever {\mc GNU} {\tt readline} is available it is used to facilitate interaction
134 with the terminal. The readline mode will be automatically disabled if |interactive|
135 is {\mc F}.
136
137 The readline specific functions are protected by |WITH_READLINE| preprocessor variable.
138
139 @c
140 #ifdef WITH_READLINE
141
142 @ Readline command generator. Each invocation must return next possible expansion of
143 |text| or {\mc NULL} if no more expansions are available. |state| is 0 if
144 the function is called for the first time for given |text|.
145 @c
146 char *
147 mixterm_command_generator(const char *text, int state)
148 {
149 static int i, len;
150 const char *name;
151
152 if (!state) {
153 i = 0;
154 len = strlen(text);
155 }
156
157 while ((name = cword[i].longname)) {
158 if (cword[i].shortname && strlen (cword[i].shortname) > strlen(name))
159 name = cword[i].shortname;
160 i++;
161 if (strncasecmp (name, text, len) == 0)
162 return strdup(name);
163 }
164
165 return NULL;
166 }
167
168 @ Prepare a NULL terminated array of possible command completions. |cmd| is the
169 command text entered so far, |start| and |end| delimit the part which should be
170 completed.
171
172 @c
173 char **
174 mixterm_command_completion(char *cmd, int start, int end)
175 {
176 if (start == 0)
177 return rl_completion_matches (cmd, mixterm_command_generator);
178 return NULL;
179 }
180
181 @ Get next available character from the |stream|
182 @c
183 static int
184 mixterm_getc(FILE *stream)
185 {
186 unsigned char c;
187
188 while (1) {
189 if (read(0, &c, 1) == 1)
190 return c;
191 if (errno == EINTR)
192 break;
193 }
194 return EOF;
195 }
196
197 @ Initialize internal readline variables.
198 @c
199 void
200 mixterm_readline_init()
201 {
202 if (!interactive)
203 return;
204
205 rl_readline_name = "mixsim";
206 rl_attempted_completion_function = (CPPFunction*)mixterm_command_completion;
207 rl_getc_function = mixterm_getc;
208 }
209
210 @ End of readline-specific section
211 @c
212 #endif
213
214 @ Read next lineful of characters. Returns pointer to allocated memory location, which
215 should be freed when no longer needed.
216 @c
217 char *
218 mixterm_readline_internal()
219 {
220 char *line;
221 char *p;
222 size_t alloclen, linelen;
223
224 p = line = calloc (1, 255);
225 if (!p)
226 abort();
227 alloclen = 255;
228 linelen = 0;
229 for (;;) {
230 size_t n;
231
232 p = fgets (p, alloclen - linelen, stdin);
233
234 if (p)
235 n = strlen(p);
236 else {
237 free (line);
238 return NULL;
239 }
240
241 linelen += n;
242
243 /* Error. */
244 if (linelen == 0) {
245 free (line);
246 return NULL;
247 }
248
249 /* Ok. */
250 if (line[linelen - 1] == '\n') {
251 line[linelen - 1] = '\0';
252 return line;
253 } else {
254 char *tmp;
255 alloclen *= 2;
256 tmp = realloc(line, alloclen);
257 if (tmp == NULL)
258 abort();
259 line = tmp;
260 p = line + linelen;
261 }
262 }
263 }
264
265 @ A wrapper over |readline| call.
266 @c
267 char *
268 mixterm_readline (char *prompt)
269 {
270 if (interactive)
271 return readline(prompt);
272 return mixterm_readline_internal();
273 }
274
275 @ When {\tt readline} is not available, provide a simplified version.
276 @c
277 #ifndef WITH_READLINE
278 char *
279 readline(char *prompt)
280 {
281 if (prompt) {
282 printf("%s", prompt);
283 fflush(stdout);
284 }
285
286 return mixterm_readline_internal();
287 }
288
289 #define mixterm_readline_init()
290 #endif
291
292
293 @* Command line parser.
294
295 @ Copy current token into |token.string|
296 @c
297 void
298 copy_token()
299 {
300 char *p;
301
302 p = token.string;
303 while (*curp && !isspace(*curp))
304 *p++ = *curp++;
305 *p = 0;
306 }
307
308 @ Advance |curp| to the next token. Copy current token into
309 |token.string| and store its type in |token.type|. Return token
310 type.
311 @c
312 int
313 nextkn()
314 {
315 if (!curp || !*curp) {
316 free(buffer);
317 curp = buffer = readline(prompt);
318 #ifdef WITH_READLINE
319 if (curp)
320 add_history(curp);
321 #endif
322 }
323
324 if (!curp)
325 return 0;
326
327 while (*curp && (*curp == ' ' || *curp == '\t'))
328 curp++;
329
330 if (isalpha(*curp) || *curp == '/') {
331 struct command_word *cwp;
332
333 copy_token();
334 for (cwp = cword; cwp->longname; cwp++) {
335 if (strcasecmp(token.string, cwp->longname) == 0
336 || (cwp->shortname
337 && strcasecmp(token.string, cwp->shortname) == 0))
338 return token.type = cwp->tok;
339
340 }
341 return token.type = T_STRING;
342 }
343 if (isdigit(*curp)) {
344 copy_token();
345 token.number = strtol(token.string, NULL, 0);
346 return token.type = T_NUMBER;
347 }
348 return token.type = *curp++;
349 }
350
351 @ Get MIX memory address from |token.number|. Bail out if the address
352 is not valid
353 @c
354 int
355 getaddr()
356 {
357 int addr = token.number;
358
359 if (addr < 0 || addr >= MEMSIZE) {
360 printf("LOCATION OUT OF ADDRESS SPACE\n");
361 return -1;
362 }
363 return addr;
364 }
365
366 @ Set a breakpoint.\par
367 The command accepts up to three arguments. In it's simplest form it is
368
369 {\parindent = 5em\indent {\sc BREAK} {\it addr}}
370
371 where {\it addr} is a valid \.{MIX} address.
372
373 Another form
374
375 {\parindent = 5em\indent {\sc BREAK} {\it addr} {\it number}}
376
377 sets the breakpoint which will be valid during next
378 {\it number} crossings.
379
380 Finally, the command
381
382 {\parindent = 5em\indent {\sc BREAK} {\sc TEMP} {\it addr}}
383
384 sets a temporary breakpoint, i.e. the one that will be removed after
385 it is crossed. It is equivalent to
386
387 {\parindent = 5em\indent {\sc BREAK} {\it addr} 1}
388
389 @c
390 void
391 m_break()
392 {
393 int addr;
394 int count = -1;
395
396 if (nextkn() == T_TEMP) {
397 count = 1;
398 nextkn();
399 }
400 if (token.type != T_NUMBER) {
401 printf("USAGE: BREAK [TEMP] <ADDR> [<COUNT>]\n");
402 return;
403 }
404 if ((addr = getaddr()) == -1)
405 return;
406 nextkn();
407 if (count == -1 && token.type == T_NUMBER) {
408 count = token.number;
409 nextkn();
410 }
411 if (token.type != T_EOL) {
412 printf("USAGE: BREAK [TEMP] <ADDR> [<COUNT>]\n");
413 return;
414 }
415 set_bp(addr, count);
416 }
417
418 @ A shorthand notation for BREAK TEMP.
419 @c
420 void
421 m_bt()
422 {
423 int addr;
424
425 if (nextkn() != T_NUMBER) {
426 printf("USAGE: BT <ADDR>\n");
427 return;
428 }
429 if ((addr = getaddr()) == -1)
430 return;
431 set_bp(addr, 1);
432 }
433
434 @ Clear a breakpoint.\par
435 {\parindent=5em\indent{\sc CLEAR BREAK {\it addr}}}
436
437 Breakpoints are distinguished by address they are set to. The addresses can be
438 obtained by {\sc LIST BREAK} command.
439
440 {\it FIXME}: We should have the ability to distinguish breakpoints by their sequence
441 numbers, as |gdb| does.
442
443 @c
444 void
445 m_clear()
446 {
447 int addr;
448
449 if (nextkn() != T_BREAK) {
450 printf("EXPECTED BREAK\n");
451 return;
452 }
453 if (nextkn() != T_NUMBER
454 || (addr = getaddr()) == -1
455 || nextkn() != T_EOL) {
456 printf("USAGE: CLEAR BREAK <ADDR>\n");
457 return;
458 }
459 clear_bp(addr);
460 }
461
462 @ Shorthand notation for CLEAR BREAK.\par
463 {\parindent=5em\indent{\sc CB {\it addr}}}
464
465 @c
466 void
467 m_cb()
468 {
469 int addr;
470
471 if (nextkn() != T_NUMBER
472 || (addr = getaddr()) == -1
473 || nextkn() != T_EOL) {
474 printf("USAGE: CB <ADDR>\n");
475 return;
476 }
477 clear_bp(addr);
478 }
479
480 @ List breakpoints
481 @c
482 void
483 m_lb()
484 {
485 if (nextkn() != T_EOL) {
486 printf("EOL MISSING\n");
487 return;
488 }
489 list_bp();
490 }
491
492 @ List I/O devices
493 @c
494 void
495 m_lio()
496 {
497 int dev = -1;
498
499 if (nextkn() == T_NUMBER) {
500 dev = token.number;
501 nextkn();
502 }
503 if (token.type != T_EOL) {
504 printf("EOL MISSING\n");
505 return;
506 }
507 list_io(dev);
508 }
509
510 @ List command.\par
511 There are three variants of this command:
512
513 \tabskip=1em \halign{%
514 \strut\sc#\hfil&\strut{\raggedright#}\cr
515 LIST IO & Lists all existing I/O devices\cr
516 LIST IO {\it num} & lists the device number {\it num}\cr
517 LIST BREAK & lists all breakpoints\cr}
518
519 @c
520 void
521 m_list()
522 {
523 switch (nextkn()) {
524 case T_IO:
525 m_lio();
526 break;
527 case T_BREAK:
528 m_lb();
529 break;
530 default:
531 printf("NOT RECOGNIZED\n");
532 }
533 }
534
535 @ Execute next \.{MIX} instruction
536 @c
537 void
538 m_next()
539 {
540 nextkn();
541 if (token.type != T_EOL) {
542 printf("EOL MISSING\n");
543 return;
544 }
545 set_next(F);
546 run();
547 }
548
549 @ Execute next \.{MIX} instruction. If it is a call, step into the called function.
550 @c
551 void
552 m_step()
553 {
554 if (nextkn() != T_EOL) {
555 printf("EOL MISSING\n");
556 return;
557 }
558 set_next(T);
559 run();
560 }
561
562 @ Continue after crossing a breakpoint
563 @c
564 void
565 m_cont()
566 {
567 if (nextkn() != T_EOL) {
568 printf("EOL MISSING\n");
569 return;
570 }
571 run();
572 }
573
574 @ Assign \UNIX/ file name to \.{MIX} device number
575 @c
576 void
577 m_asgn()
578 {
579 int devno;
580
581 if (nextkn() != T_NUMBER) {
582 printf("ASGN <DEV> <UNIX-PATH>\n");
583 return;
584 }
585 devno = token.number;
586 if (nextkn() == T_EOL) {
587 printf("ASGN <DEV> <UNIX-PATH>\n");
588 return;
589 }
590 asgn_io(devno, token.string);
591 }
592
593 @ Emulate \.{MIX} \.{GO} button
594 @c
595 void
596 m_go()
597 {
598 if (nextkn() != T_EOL) {
599 printf("EOL MISSING\n");
600 return;
601 }
602 go();
603 }
604
605 @ Dump registers.
606 @c
607 void
608 m_dr()
609 {
610 if (nextkn() != T_EOL) {
611 printf("EOL MISSING\n");
612 return;
613 }
614 dump_status();
615 }
616
617 @ Dump memory range.\par
618 {\parindent=5em\indent{\sc DM} [{\it addr} [{\it addr}]]}
619 Optional arguments specify start and end addresses of
620 the memory block to be displayed. The addresses are rounded to the nearest memory
621 boundary divisible by 5 (see |@<Diagnostic functions@>|.)
622
623 If no arguments are given, prints the entire \.{MIX} memory.
624
625 @c
626 void
627 m_dm()
628 {
629 int start_loc = 0, end_loc = MEMSIZE;
630
631 if (nextkn() == T_NUMBER) {
632 if ((start_loc = getaddr()) == -1) {
633 return;
634 }
635 if (nextkn() == T_NUMBER) {
636 if ((end_loc = getaddr()) == -1)
637 return;
638 nextkn();
639 }
640 }
641 if (end_loc < start_loc) {
642 printf("BAD RANGE\n");
643 return;
644 }
645 if (token.type != T_EOL) {
646 printf("EOL MISSING\n");
647 return;
648 }
649 dump_memory(start_loc, end_loc);
650 }
651
652 @ Dump memory or registers
653 @c
654 void
655 m_dump()
656 {
657 switch (nextkn()) {
658 case T_MEMORY:
659 m_dm();
660 break;
661 case T_REGISTERS:
662 m_dr();
663 break;
664 default:
665 dump();
666 break;
667 }
668 }
669
670 @ Disassemble the memory range
671 {\parindent=5em{\sc UNASM} [{\it addr} [{\it addr}]]}
672 If no arguments are specified, disassembles the next instruction.
673 Short notation: {\sc U}
674
675 @c
676 void
677 m_unasm()
678 {
679 extern int loc;
680 int start_loc = loc, end_loc = loc+5;
681
682 if (nextkn() == T_NUMBER) {
683 if ((start_loc = getaddr()) == -1)
684 return;
685 if (nextkn() == T_NUMBER) {
686 if ((end_loc = getaddr()) == -1)
687 return;
688 nextkn();
689 } else
690 end_loc = start_loc + 5;
691 }
692 if (end_loc < start_loc) {
693 printf("BAD RANGE\n");
694 return;
695 }
696 if (token.type != T_EOL) {
697 printf("EOL MISSING\n");
698 return;
699 }
700 disas(stdout, start_loc, end_loc);
701 }
702
703 @ Exit the terminal.
704 @c
705 void
706 m_quit()
707 {
708 exit(0);
709 }
710
711 @ Process end of line.
712 Does nothing, currently;
713
714 @c
715 void
716 m_eol()
717 {
718 }
719
720 @ Indicate syntax error.
721 @c
722 void
723 m_err()
724 {
725 printf("NOT RECOGNIZED\n");
726 curp = NULL;
727 }
728
729 @ Dispatcher table for MIX terminal commands
730 @c
731 struct disp {
732 int tok; /* Token type */
733 void (*fun)(void); /* Handler function */
734 } main_disp[] = {
735 { T_BREAK, m_break }, @/
736 { T_BT, m_bt }, @/
737 { T_CLEAR, m_clear }, @/
738 { T_CB, m_cb }, @/
739 { T_LIST, m_list }, @/
740 { T_LB, m_lb }, @/
741 { T_NEXT, m_next }, @/
742 { T_STEP, m_step }, @/
743 { T_ASGN, m_asgn }, @/
744 { T_GO, m_go }, @/
745 { T_DUMP, m_dump }, @/
746 { T_DR, m_dr }, @/
747 { T_DM, m_dm }, @/
748 { T_LIST, m_list }, @/
749 { T_LI, m_lio }, @/
750 { T_QUIT, m_quit }, @/
751 { T_EOL, m_eol }, @/
752 { T_CONT, m_cont }, @/
753 { T_UNASM, m_unasm }, @/
754 { 0 } @/
755 };
756
757 @ MIX terminal prompt. In homage to IBM 360 \hbox to 2em{\hfill $\smile$ }{\hskip -1em}\"
758 @c
759 char init_prompt[] = "\nMIX TERMINAL STATION READY FOR COMMUNICATION\n";
760
761 @ Main terminal function
762 @c
763 void
764 mixterm()
765 {
766 struct disp *disp;
767 void (*fun)();
768
769 interactive = isatty(0);
770 mixterm_readline_init();
771 printf("%s\n", init_prompt);
772 while (nextkn()) {
773 fun = m_err;
774 for (disp = main_disp; disp->tok; disp++) {
775 if (token.type == disp->tok) {
776 fun = disp->fun;
777 break;
778 }
779 }
780 (*fun)();
781 }
782 }

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

Send suggestions and bug reports to Sergey Poznyakoff
ViewVC Help
Powered by ViewVC 1.1.20