1use self::emu::*;
5use self::misc::*;
6use self::utils::*;
7use b2c2_casl2 as casl2;
8use b2c2_compiler as compiler;
9use b2c2_flag::Flags;
10use b2c2_jis_x_201 as jis_x_201;
11use b2c2_parser as parser;
12use b2c2_stat as stat;
13use b2c2_tokenizer as tokenizer;
14use ext::*;
15use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
16use std::convert::TryFrom;
17use std::fmt;
18use std::fs;
19use std::io::{self, BufRead, Write};
20use std::path::{Path, PathBuf};
21
22mod emu;
23mod ext;
24mod misc;
25mod utils;
26
27const RET_OP_CODE: u16 = 0x8100;
28const REQUEST_CONTINUE: i32 = 0x300_0000;
29const REQUEST_BREAK: i32 = 0x400_0000;
30const REQUEST_QUIT: i32 = 99;
31const DEFAULT_COMET2_LIMIT_ON_BASIC_MODE: u64 = 1_000_000_000_000;
32const DEFAULT_STEP_LIMIT: u64 = 100_000_000;
33
34#[derive(Clone, Copy)]
35enum RunMode {
36 Step(u64),
37 GoToBreakPoint(u64),
38 SkipSubroutine(u64),
39 BasicStep {
40 basic_step_count: u64,
41 comet2_limit: u64,
42 },
43 GoToBasicBreakPoint {
44 basic_limit: u64,
45 comet2_limit: u64,
46 },
47 SkipBasicSubroutine {
48 basic_limit: u64,
49 comet2_limit: u64,
50 },
51}
52
53#[derive(Clone, Copy)]
54enum DebugMode {
55 Basic,
56 Casl2,
57}
58
59struct State {
60 err: Option<RuntimeError>,
61 run_mode: RunMode,
62 start_point: Option<usize>,
63 default_cmd: Vec<Option<String>>,
64 debug_mode: DebugMode,
65}
66
67enum Code {
68 Casl2(casl2::Command),
69 In(String),
70 Out(String),
71}
72
73enum ViewType {
74 Bool,
75 Int,
76 Str,
77}
78
79enum ExtendedLabel {
80 GlobalLabel(String),
81 LocalLabel(String, String),
82 GeneralRegister(casl2::Register),
83 ProgramRegister,
84 StackPointer,
85 DecConst(u16),
86 HexConst(u16),
87 Load(Box<ExtendedLabel>),
88 Sum(Box<ExtendedLabel>, Box<ExtendedLabel>),
89 Diff(Box<ExtendedLabel>, Box<ExtendedLabel>),
90}
91
92enum Value {
93 Int(u16),
94 Str(String),
95}
96
97pub fn run_nonstep(src_file: String, flags: Flags) -> io::Result<i32> {
98 let stdin = io::stdin();
99 let mut stdin = stdin.lock();
100 let stdout = io::stdout();
101 let mut stdout = stdout.lock();
102 let mut emu = Emulator::new();
103
104 emu.debug_mode = false;
105
106 match if src_file.to_ascii_lowercase().ends_with(".cas") {
107 emu.compile_casl2(&src_file, &flags, false)
108 } else {
109 emu.compile_basic(&src_file, &flags, false)
110 } {
111 Ok(0) => {}
112 result => return result,
113 }
114
115 if emu.start_point.is_none() {
116 eprintln!("入力ファイルが正しくありません");
117 return Ok(101);
118 }
119
120 if !emu.unknown_labels.is_empty() {
121 let src_dir = Path::new(&src_file).parent().unwrap();
123 match auto_resolve_files(&mut emu, &mut stdin, &mut stdout, src_dir, &flags) {
124 Ok(0) => {}
125 Ok(REQUEST_QUIT) => return Ok(0),
126 result => return result,
127 }
128 }
129
130 emu.init_to_start(None);
131
132 loop {
133 match emu.step_through_code(&mut stdin, &mut stdout) {
134 Ok(_) => {}
135 Err(RuntimeError::IoError(error)) => return Err(error),
136 Err(error) => {
137 eprintln!("{}", error);
138 if matches!(error, RuntimeError::NormalTermination { .. }) {
139 return Ok(0);
140 } else {
141 return Ok(REQUEST_QUIT);
142 }
143 }
144 }
145 }
146}
147
148pub fn run_basic(src_file: String, flags: Flags) -> io::Result<i32> {
149 let stdin = io::stdin();
150 let mut stdin = stdin.lock();
151 let stdout = io::stdout();
152 let mut stdout = stdout.lock();
153
154 loop {
155 let mut emu = Emulator::new();
156
157 match if src_file.to_ascii_lowercase().ends_with(".cas") {
158 emu.compile_casl2(&src_file, &flags, true)
159 } else {
160 emu.compile_basic(&src_file, &flags, true)
161 } {
162 Ok(0) => {}
163 result => return result,
164 }
165
166 if emu.start_point.is_none() {
167 eprintln!("入力ファイルが正しくありません");
168 return Ok(101);
169 }
170
171 if !emu.unknown_labels.is_empty() {
172 let src_dir = Path::new(&src_file).parent().unwrap();
174 match resolve_files(&mut emu, &mut stdin, &mut stdout, src_dir, &flags) {
175 Ok(0) => {}
176 Ok(REQUEST_QUIT) => return Ok(0),
177 result => return result,
178 }
179 }
180
181 emu.init_to_start(None);
182
183 let mut state = State {
184 err: None,
185 run_mode: RunMode::BasicStep {
186 basic_step_count: 1,
187 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
188 },
189 start_point: None,
190 default_cmd: vec![None, None],
191 debug_mode: DebugMode::Basic,
192 };
193
194 loop {
195 writeln!(stdout)?;
196 match state.debug_mode {
197 DebugMode::Basic => {
198 show_state_basic(&emu, &mut stdout, &state)?;
199 if let Some((file, label, _)) = emu.get_current_program() {
200 if emu.basic_info.contains_key(file) {
201 show_var_for_basic(&emu, &mut stdout, Some(label.as_str()))?;
202 }
203 }
204
205 match interactive_basic(&mut emu, &mut stdin, &mut stdout, &mut state) {
206 Ok(0) => {}
207 Ok(REQUEST_CONTINUE) => {
208 if matches!(state.debug_mode, DebugMode::Casl2) {
209 writeln!(stdout, "CASL2デバッグモードに切り替えます")?;
210 }
211 continue;
212 }
213 Ok(REQUEST_BREAK) => break,
214 Ok(REQUEST_QUIT) => return Ok(0),
215 result => return result,
216 }
217 }
218 DebugMode::Casl2 => {
219 show_state(&emu, &mut stdout, &state)?;
220 show_reg(&emu, &mut stdout)?;
221
222 match interactive_casl2(&mut emu, &mut stdin, &mut stdout, &mut state, true) {
223 Ok(0) => {}
224 Ok(REQUEST_CONTINUE) => {
225 if matches!(state.debug_mode, DebugMode::Basic) {
226 writeln!(stdout, "BASICデバッグモードに切り替えます")?;
227 }
228 continue;
229 }
230 Ok(REQUEST_BREAK) => break,
231 Ok(REQUEST_QUIT) => return Ok(0),
232 result => return result,
233 }
234 }
235 }
236
237 writeln!(stdout)?;
238
239 if state.err.is_some() {
240 writeln!(stdout, "プログラムは終了状態です")?;
241 } else {
242 let result = match state.run_mode {
243 RunMode::SkipSubroutine(limit) => {
244 do_skip_subroutine(&mut emu, &mut stdin, &mut stdout, &mut state, limit)
245 }
246 RunMode::GoToBreakPoint(limit) => {
247 do_go_to_breakpoint(&mut emu, &mut stdin, &mut stdout, &mut state, limit)
248 }
249 RunMode::Step(count) => {
250 do_step(&mut emu, &mut stdin, &mut stdout, &mut state, count)
251 }
252 RunMode::BasicStep {
253 basic_step_count,
254 comet2_limit,
255 } => do_step_basic(
256 &mut emu,
257 &mut stdin,
258 &mut stdout,
259 &mut state,
260 basic_step_count,
261 comet2_limit,
262 ),
263 RunMode::GoToBasicBreakPoint {
264 basic_limit,
265 comet2_limit,
266 } => do_go_to_breakpoint_for_basic(
267 &mut emu,
268 &mut stdin,
269 &mut stdout,
270 &mut state,
271 basic_limit,
272 comet2_limit,
273 ),
274 RunMode::SkipBasicSubroutine {
275 basic_limit,
276 comet2_limit,
277 } => do_skip_subroutine_for_basic(
278 &mut emu,
279 &mut stdin,
280 &mut stdout,
281 &mut state,
282 basic_limit,
283 comet2_limit,
284 ),
285 };
286 match result {
287 Ok(0) => {}
288 result => return result,
289 }
290 }
291 }
292 }
293}
294
295pub fn run_casl2(src_file: String, flags: Flags) -> io::Result<i32> {
296 let stdin = io::stdin();
297 let mut stdin = stdin.lock();
298 let stdout = io::stdout();
299 let mut stdout = stdout.lock();
300
301 loop {
302 let mut emu = Emulator::new();
303
304 match if src_file.to_ascii_lowercase().ends_with(".cas") {
305 emu.compile_casl2(&src_file, &flags, true)
306 } else {
307 emu.compile_basic(&src_file, &flags, true)
308 } {
309 Ok(0) => {}
310 result => return result,
311 }
312
313 if emu.start_point.is_none() {
314 eprintln!("入力ファイルが正しくありません");
315 return Ok(101);
316 }
317
318 if !emu.unknown_labels.is_empty() {
319 let src_dir = Path::new(&src_file).parent().unwrap();
321 match resolve_files(&mut emu, &mut stdin, &mut stdout, src_dir, &flags) {
322 Ok(0) => {}
323 Ok(REQUEST_QUIT) => return Ok(0),
324 result => return result,
325 }
326 }
327
328 emu.init_to_start(None);
329
330 let mut state = State {
331 err: None,
332 run_mode: RunMode::Step(1),
333 start_point: None,
334 default_cmd: vec![None, None],
335 debug_mode: DebugMode::Casl2,
336 };
337
338 loop {
339 writeln!(stdout)?;
340 show_state(&emu, &mut stdout, &state)?;
341 show_reg(&emu, &mut stdout)?;
342
343 match interactive_casl2(&mut emu, &mut stdin, &mut stdout, &mut state, false) {
344 Ok(0) => {}
345 Ok(REQUEST_CONTINUE) => {
346 if matches!(state.debug_mode, DebugMode::Basic) {
347 writeln!(stdout, "BASICデバッグモードは使用できません")?;
348 state.debug_mode = DebugMode::Casl2;
349 }
350 continue;
351 }
352 Ok(REQUEST_BREAK) => break,
353 Ok(REQUEST_QUIT) => return Ok(0),
354 result => return result,
355 }
356
357 writeln!(stdout)?;
358
359 if state.err.is_some() {
360 writeln!(stdout, "プログラムは終了状態です")?;
361 } else {
362 let result = match state.run_mode {
363 RunMode::SkipSubroutine(limit) => {
364 do_skip_subroutine(&mut emu, &mut stdin, &mut stdout, &mut state, limit)
365 }
366 RunMode::GoToBreakPoint(limit) => {
367 do_go_to_breakpoint(&mut emu, &mut stdin, &mut stdout, &mut state, limit)
368 }
369 RunMode::Step(count) => {
370 do_step(&mut emu, &mut stdin, &mut stdout, &mut state, count)
371 }
372 RunMode::BasicStep { .. }
373 | RunMode::GoToBasicBreakPoint { .. }
374 | RunMode::SkipBasicSubroutine { .. } => {
375 unreachable!("BUG")
376 }
377 };
378 match result {
379 Ok(0) => {}
380 result => return result,
381 }
382 }
383 }
384 }
385}
386
387fn do_go_to_breakpoint_for_basic<R: BufRead, W: Write>(
391 emu: &mut Emulator,
392 stdin: &mut R,
393 stdout: &mut W,
394 state: &mut State,
395 basic_limit: u64,
396 comet2_limit: u64,
397) -> io::Result<i32> {
398 let mut step_limit = basic_limit;
399 let mut limit = comet2_limit;
400 let mut reach = false;
401 while step_limit > 0 && limit > 0 {
402 limit -= 1;
403 match emu.step_through_code(stdin, stdout) {
404 Ok(_) => {}
405 Err(RuntimeError::IoError(error)) => return Err(error),
406 Err(error) => {
407 let last_pos = emu.last_run_position;
408 let last_op_code = emu.mem[last_pos];
409 if emu.break_points[last_pos] {
410 reach = true;
411 } else if get_op_code_size(last_op_code) == 2 {
412 reach = emu.break_points[last_pos + 1];
413 }
414 state.err = Some(error);
415 break;
416 }
417 }
418 if emu.basic_step.is_some() {
419 step_limit -= 1;
420 limit = comet2_limit;
421 if emu.break_points[emu.last_run_position] {
422 reach = true;
423 break;
424 }
425 }
426 }
427 writeln!(stdout)?;
428 if reach {
429 writeln!(stdout, "ブレークポイントに到達しました")?;
430 } else if state.err.is_some() {
431 if matches!(state.err, Some(RuntimeError::NormalTermination { .. })) {
432 writeln!(stdout, "ブレークポイント到達前にプログラムが終了しました")?;
433 } else {
434 writeln!(stdout, "実行はエラーで停止しました")?;
435 }
436 } else if step_limit == 0 {
437 writeln!(
438 stdout,
439 "指定ステップ数が終わる前にステップ数の制限({})に達して停止しました",
440 basic_limit
441 )?;
442 } else if limit == 0 {
443 writeln!(
444 stdout,
445 "指定ステップ数が終わる前にCOMET2ステップ数制限({})に達して停止しました",
446 comet2_limit
447 )?;
448 }
449 Ok(0)
450}
451
452fn do_step_basic<R: BufRead, W: Write>(
456 emu: &mut Emulator,
457 stdin: &mut R,
458 stdout: &mut W,
459 state: &mut State,
460 mut basic_step_count: u64,
461 comet2_limit: u64,
462) -> io::Result<i32> {
463 let mut limit = comet2_limit;
464 while basic_step_count > 0 && limit > 0 {
465 limit -= 1;
466 match emu.step_through_code(stdin, stdout) {
467 Ok(_) => {}
468 Err(RuntimeError::IoError(error)) => return Err(error),
469 Err(error) => {
470 state.err = Some(error);
471 basic_step_count -= 1;
472 break;
473 }
474 }
475 if emu.basic_step.is_some() {
476 basic_step_count -= 1;
477 limit = comet2_limit;
478 }
479 }
480 if basic_step_count != 0 {
481 if state.err.is_some() {
482 if matches!(state.err, Some(RuntimeError::NormalTermination { .. })) {
483 writeln!(stdout, "指定ステップ数が終わる前にプログラムが終了しました")?;
484 } else {
485 writeln!(stdout, "指定ステップ数が終わる前にエラーで停止しました")?;
486 }
487 } else if limit == 0 {
488 writeln!(
489 stdout,
490 "指定ステップ数が終わる前にCOMET2ステップ数制限({})に達して停止しました",
491 comet2_limit
492 )?;
493 }
494 }
495 Ok(0)
496}
497
498fn do_skip_subroutine_for_basic<R: BufRead, W: Write>(
502 emu: &mut Emulator,
503 stdin: &mut R,
504 stdout: &mut W,
505 state: &mut State,
506 basic_limit: u64,
507 comet2_limit: u64,
508) -> io::Result<i32> {
509 let nest = emu.program_stack.len();
510 let mut step_limit = basic_limit;
511 let mut limit = comet2_limit;
512 let mut reach = false;
513 while step_limit > 0 && limit > 0 {
514 limit -= 1;
515 match emu.step_through_code(stdin, stdout) {
516 Ok(_) => {}
517 Err(RuntimeError::IoError(error)) => return Err(error),
518 Err(error) => {
519 reach = emu.program_stack.len() < nest;
520 state.err = Some(error);
521 break;
522 }
523 }
524 if emu.basic_step.is_some() {
525 step_limit -= 1;
526 limit = comet2_limit;
527 if emu.program_stack.len() < nest {
528 reach = true;
529 break;
530 }
531 }
532 }
533 writeln!(stdout)?;
534 if reach {
535 writeln!(stdout, "スキップ実行はサブルーチンを脱出し停止しました")?;
536 } else if state.err.is_some() {
537 writeln!(stdout, "スキップ実行はエラーで停止しました")?;
538 } else if step_limit == 0 {
539 writeln!(
540 stdout,
541 "スキップ実行はステップ制限数({})に到達で停止しました",
542 basic_limit
543 )?;
544 } else if limit == 0 {
545 writeln!(
546 stdout,
547 "スキップ実行はCOMET2ステップ制限数({})に到達で停止しました",
548 comet2_limit
549 )?;
550 }
551 Ok(0)
552}
553
554fn do_step<R: BufRead, W: Write>(
558 emu: &mut Emulator,
559 stdin: &mut R,
560 stdout: &mut W,
561 state: &mut State,
562 mut limit: u64,
563) -> io::Result<i32> {
564 while limit > 0 {
565 limit -= 1;
566 match emu.step_through_code(stdin, stdout) {
567 Ok(_) => {}
568 Err(RuntimeError::IoError(error)) => return Err(error),
569 Err(error) => {
570 state.err = Some(error);
571 break;
572 }
573 }
574 }
575 if state.err.is_some() && limit != 0 {
576 if matches!(state.err, Some(RuntimeError::NormalTermination { .. })) {
577 writeln!(stdout, "指定ステップ数が終わる前にプログラムが終了しました")?;
578 } else {
579 writeln!(stdout, "指定ステップ数が終わる前にエラーで停止しました")?;
580 }
581 }
582 Ok(0)
583}
584
585fn do_go_to_breakpoint<R: BufRead, W: Write>(
589 emu: &mut Emulator,
590 stdin: &mut R,
591 stdout: &mut W,
592 state: &mut State,
593 step_limit: u64,
594) -> io::Result<i32> {
595 let mut limit = step_limit;
596 let mut reach = false;
597 while limit > 0 {
598 limit -= 1;
599 match emu.step_through_code(stdin, stdout) {
600 Ok(_) => {
601 let last_pos = emu.last_run_position;
602 let last_op_code = emu.mem[last_pos];
603 if emu.break_points[last_pos]
604 || (get_op_code_size(last_op_code) == 2 && emu.break_points[last_pos + 1])
605 {
606 reach = true;
607 break;
608 }
609 }
610 Err(RuntimeError::IoError(error)) => return Err(error),
611 Err(error) => {
612 let last_pos = emu.last_run_position;
613 let last_op_code = emu.mem[last_pos];
614 if emu.break_points[last_pos] {
615 reach = true;
616 } else if get_op_code_size(last_op_code) == 2 {
617 reach = emu.break_points[last_pos + 1];
618 }
619 state.err = Some(error);
620 break;
621 }
622 }
623 }
624 writeln!(stdout)?;
625 if reach {
626 writeln!(stdout, "ブレークポイントに到達しました")?;
627 } else if state.err.is_some() {
628 if matches!(state.err, Some(RuntimeError::NormalTermination { .. })) {
629 writeln!(stdout, "ブレークポイント到達前にプログラムが終了しました")?;
630 } else {
631 writeln!(stdout, "実行はエラーで停止しました")?;
632 }
633 } else if limit == 0 {
634 writeln!(
635 stdout,
636 "実行はステップ制限数({})に到達で停止しました",
637 step_limit
638 )?;
639 }
640 Ok(0)
641}
642
643fn do_skip_subroutine<R: BufRead, W: Write>(
647 emu: &mut Emulator,
648 stdin: &mut R,
649 stdout: &mut W,
650 state: &mut State,
651 step_limit: u64,
652) -> io::Result<i32> {
653 let nest = emu.program_stack.len();
654 let mut limit = step_limit;
655 let mut reach = false;
656 while limit > 0 {
657 limit -= 1;
658 match emu.step_through_code(stdin, stdout) {
659 Ok(_) => {
660 let last_pos = emu.last_run_position;
661 let last_op_code = emu.mem[last_pos];
662 if last_op_code == RET_OP_CODE && nest == emu.program_stack.len() + 1 {
663 reach = true;
664 break;
665 }
666 }
667 Err(RuntimeError::IoError(error)) => return Err(error),
668 Err(error) => {
669 let last_pos = emu.last_run_position;
670 let last_op_code = emu.mem[last_pos];
671 reach = last_op_code == RET_OP_CODE && nest == emu.program_stack.len() + 1;
672 state.err = Some(error);
673 break;
674 }
675 }
676 }
677 writeln!(stdout)?;
678 if reach {
679 writeln!(stdout, "スキップ実行はRETに到達し停止しました")?;
680 } else if state.err.is_some() {
681 writeln!(stdout, "スキップ実行はエラーで停止しました")?;
682 } else if limit == 0 {
683 writeln!(
684 stdout,
685 "スキップ実行はステップ制限数({})に到達で停止しました",
686 step_limit
687 )?;
688 }
689 Ok(0)
690}
691
692fn interactive_basic<R: BufRead, W: Write>(
696 emu: &mut Emulator,
697 stdin: &mut R,
698 stdout: &mut W,
699 state: &mut State,
700) -> io::Result<i32> {
701 let mut line = String::new();
702
703 let command_list = {
704 use std::fmt::Write;
705 let mut lines = String::new();
706 let mut tmp = String::new();
707 for cmd in &[
708 "default-cmd",
709 "fill-arr",
710 "help",
711 "list-files",
712 "mode",
713 "quit",
714 "remove-breakpoint",
715 "reset",
716 "restart",
717 "run",
718 "set-breakpoint",
719 "set-by-file",
720 "set-elem",
721 "set-len",
722 "set-start",
723 "set-var",
724 "show-execute-stat",
725 "show-src",
726 "show-state",
727 "show-var",
728 "skip",
729 "step",
730 ] {
731 if tmp.len() + cmd.len() + 1 >= 80 {
732 writeln!(&mut lines, "{}", tmp).unwrap();
733 tmp.clear();
734 }
735 tmp.push_str(cmd);
736 tmp.push(' ');
737 }
738 if !tmp.is_empty() {
739 writeln!(&mut lines, "{}", tmp).unwrap();
740 }
741 lines
742 };
743
744 loop {
745 writeln!(stdout)?;
746 writeln!(stdout, "使用可能なデバッガコマンド:")?;
747 writeln!(stdout, "{}", command_list)?;
748 writeln!(stdout)?;
749 write!(stdout, "[BASIC] > ")?;
750 stdout.flush()?;
751 line.clear();
752 if stdin.read_line(&mut line)? == 0 {
753 eprintln!("入力がキャンセルされました");
754 io::stderr().flush()?;
755 writeln!(stdout, "テスト実行を中止します")?;
756 return Ok(REQUEST_QUIT);
757 }
758 let mut line = line.trim().to_string();
759 if line.is_empty() {
760 if let Some(defcmd) = state.default_cmd[DebugMode::Basic as usize].as_ref() {
761 line = defcmd.clone();
762 }
763 }
764 let mut cmd_and_param = line.splitn(2, ' ').map(|s| s.trim());
765 let cmd = cmd_and_param.next().unwrap();
766 match cmd {
767 "default-cmd" => {
768 if let Some(defcmd) = cmd_and_param.next() {
769 if "none".eq_ignore_ascii_case(defcmd) {
770 state.default_cmd[DebugMode::Basic as usize] = None;
771 } else {
772 state.default_cmd[DebugMode::Basic as usize] = Some(defcmd.to_string());
773 }
774 }
775 if let Some(defcmd) = state.default_cmd[DebugMode::Basic as usize].as_ref() {
776 writeln!(stdout, "デフォルトのデバッガコマンド: {}", defcmd)?;
777 } else {
778 writeln!(stdout, "デフォルトのデバッガコマンド: none")?;
779 }
780 }
781 "fill-arr" => fill_arr(emu, stdout, cmd_and_param.next())?,
782 "help" => show_command_help_for_basic(cmd_and_param.next(), stdout)?,
783 "list-files" => list_files(emu, stdout)?,
784 "mode" => {
785 if let Some(param) = cmd_and_param.next() {
786 if "casl2".eq_ignore_ascii_case(param) {
787 state.debug_mode = DebugMode::Casl2;
788 return Ok(REQUEST_CONTINUE);
789 } else if "basic".eq_ignore_ascii_case(param) {
790 writeln!(stdout, "現在BASICデバッグモードです")?;
791 } else {
792 writeln!(stdout, "引数が不正です")?;
793 }
794 } else {
795 writeln!(stdout, "現在BASICデバッグモードです")?;
796 }
797 }
798 "quit" => {
799 writeln!(stdout, "テスト実行を中止します")?;
800 return Ok(REQUEST_QUIT);
801 }
802 "remove-breakpoint" => {
803 set_breakpoint_for_basic(emu, stdout, cmd_and_param.next(), false)?
804 }
805 "reset" => {
806 writeln!(stdout, "エミュレータをリセットします")?;
807 writeln!(stdout)?;
808 return Ok(REQUEST_BREAK);
809 }
810 "restart" => {
811 emu.init_to_start(state.start_point);
812 state.err = None;
813 writeln!(stdout, "プログラムをリスタートします")?;
814 writeln!(stdout)?;
815 return Ok(REQUEST_CONTINUE);
816 }
817 "run" => {
818 if let Some(param) = cmd_and_param.next() {
819 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
820 match iter.next().unwrap().parse::<u64>() {
821 Ok(basic_limit) if basic_limit > 0 => {
822 if let Some(rest) = iter.next() {
823 match rest.parse::<u64>() {
824 Ok(comet2_limit) if comet2_limit > 0 => {
825 state.run_mode = RunMode::GoToBasicBreakPoint {
826 basic_limit,
827 comet2_limit,
828 };
829 return Ok(0);
830 }
831 _ => writeln!(stdout, "引数が不正です")?,
832 }
833 } else {
834 state.run_mode = RunMode::GoToBasicBreakPoint {
835 basic_limit,
836 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
837 };
838 return Ok(0);
839 }
840 }
841 _ => writeln!(stdout, "引数が不正です")?,
842 };
843 } else {
844 state.run_mode = RunMode::GoToBasicBreakPoint {
845 basic_limit: DEFAULT_STEP_LIMIT,
846 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
847 };
848 return Ok(0);
849 }
850 }
851 "set-breakpoint" => set_breakpoint_for_basic(emu, stdout, cmd_and_param.next(), true)?,
852 "set-by-file" => set_by_file(emu, stdout, state, cmd_and_param.next(), true)?,
853 "set-elem" => set_elem(emu, stdout, cmd_and_param.next())?,
854 "set-len" => set_len(emu, stdout, cmd_and_param.next())?,
855 "set-start" => {
856 if let Some(s) = cmd_and_param.next() {
857 let name = s.to_ascii_uppercase();
858 if let Some(pos) = emu.program_labels.get(&name) {
859 state.start_point = Some(*pos);
860 writeln!(
861 stdout,
862 "スタートポイントを{}に設定しました(次のrestartから有効です)",
863 name
864 )?;
865 } else {
866 writeln!(stdout, "プログラムエントリ名{}が見つかりません", name)?;
867 }
868 } else {
869 state.start_point = None;
870 let name = emu.start_point.as_ref().unwrap();
871 writeln!(
872 stdout,
873 "スタートポイントを{}に戻しました(次のrestartから有効です)",
874 name
875 )?;
876 }
877 }
878 "set-var" => set_var(emu, stdout, cmd_and_param.next())?,
879 "show-execute-stat" => show_execute_stat(emu, stdout, cmd_and_param.next())?,
880 "show-src" => show_src_basic(emu, stdout, cmd_and_param.next())?,
881 "show-state" => show_state_basic(emu, stdout, state)?,
882 "show-var" => show_var_for_basic(emu, stdout, cmd_and_param.next())?,
883 "skip" => {
884 if let Some(param) = cmd_and_param.next() {
885 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
886 match iter.next().unwrap().parse::<u64>() {
887 Ok(basic_limit) if basic_limit > 0 => {
888 if let Some(rest) = iter.next() {
889 match rest.parse::<u64>() {
890 Ok(comet2_limit) if comet2_limit > 0 => {
891 state.run_mode = RunMode::SkipBasicSubroutine {
892 basic_limit,
893 comet2_limit,
894 };
895 return Ok(0);
896 }
897 _ => writeln!(stdout, "引数が不正です")?,
898 }
899 } else {
900 state.run_mode = RunMode::SkipBasicSubroutine {
901 basic_limit,
902 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
903 };
904 return Ok(0);
905 }
906 }
907 _ => writeln!(stdout, "引数が不正です")?,
908 };
909 } else {
910 state.run_mode = RunMode::SkipBasicSubroutine {
911 basic_limit: DEFAULT_STEP_LIMIT,
912 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
913 };
914 return Ok(0);
915 }
916 }
917 "step" | "s" => {
918 if let Some(param) = cmd_and_param.next() {
919 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
920 match iter.next().unwrap().parse::<u64>() {
921 Ok(basic_step_count) if basic_step_count > 0 => {
922 if let Some(rest) = iter.next() {
923 match rest.parse::<u64>() {
924 Ok(comet2_limit) if comet2_limit > 0 => {
925 state.run_mode = RunMode::BasicStep {
926 basic_step_count,
927 comet2_limit,
928 };
929 return Ok(0);
930 }
931 _ => writeln!(stdout, "引数が不正です")?,
932 }
933 } else {
934 state.run_mode = RunMode::BasicStep {
935 basic_step_count,
936 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
937 };
938 return Ok(0);
939 }
940 }
941 _ => writeln!(stdout, "引数が不正です")?,
942 };
943 } else {
944 state.run_mode = RunMode::BasicStep {
945 basic_step_count: 1,
946 comet2_limit: DEFAULT_COMET2_LIMIT_ON_BASIC_MODE,
947 };
948 return Ok(0);
949 }
950 }
951 _ => {
952 writeln!(stdout, "コマンドが正しくありません")?;
953 }
954 }
955 }
956}
957
958fn interactive_casl2<R: BufRead, W: Write>(
962 emu: &mut Emulator,
963 stdin: &mut R,
964 stdout: &mut W,
965 state: &mut State,
966 is_basic_mode: bool,
967) -> io::Result<i32> {
968 let mut line = String::new();
969
970 let command_list = {
971 use std::fmt::Write;
972 let mut lines = String::new();
973 let mut tmp = String::new();
974 for cmd in vec![
975 "add-dc",
976 "add-ds",
977 "copy-mem",
978 "default-cmd",
979 "dump-code",
980 "dump-mem",
981 "fill-mem",
982 "find-code",
983 "find-src",
984 "find-value",
985 "help",
986 "list-files",
987 "mode",
988 "quit",
989 "remove-breakpoint",
990 "reset",
991 "restart",
992 "run",
993 "set-breakpoint",
994 "set-by-file",
995 "set-label",
996 "set-mem",
997 "set-reg",
998 "set-start",
999 "show-labels",
1000 "show-mem",
1001 "show-mem-stat",
1002 "show-reg",
1003 "show-src",
1004 "show-state",
1005 "show-var",
1006 "skip",
1007 "step",
1008 "write-code",
1009 ] {
1010 if tmp.len() + cmd.len() + 1 >= 80 {
1011 writeln!(&mut lines, "{}", tmp).unwrap();
1012 tmp.clear();
1013 }
1014 tmp.push_str(cmd);
1015 tmp.push(' ');
1016 }
1017 if !tmp.is_empty() {
1018 writeln!(&mut lines, "{}", tmp).unwrap();
1019 }
1020 lines
1021 };
1022
1023 loop {
1024 writeln!(stdout)?;
1025 writeln!(stdout, "使用可能なデバッガコマンド:")?;
1026 writeln!(stdout, "{}", command_list)?;
1027 writeln!(stdout)?;
1028 write!(stdout, "[CASL2] > ")?;
1029 stdout.flush()?;
1030 line.clear();
1031 if stdin.read_line(&mut line)? == 0 {
1032 eprintln!("入力がキャンセルされました");
1033 io::stderr().flush()?;
1034 writeln!(stdout, "テスト実行を中止します")?;
1035 return Ok(REQUEST_QUIT);
1036 }
1037 let mut line = line.trim().to_string();
1038 if line.is_empty() {
1039 if let Some(defcmd) = state.default_cmd[DebugMode::Casl2 as usize].as_ref() {
1040 line = defcmd.clone();
1041 }
1042 }
1043 let mut cmd_and_param = line.splitn(2, ' ').map(|s| s.trim());
1044 let cmd = cmd_and_param.next().unwrap();
1045 match cmd {
1046 "add-dc" => add_dc(emu, stdout, cmd_and_param.next())?,
1047 "add-ds" => add_ds(emu, stdout, cmd_and_param.next())?,
1048 "copy-mem" => copy_mem(emu, stdout, cmd_and_param.next())?,
1049 "default-cmd" => {
1050 if let Some(defcmd) = cmd_and_param.next() {
1051 if "none".eq_ignore_ascii_case(defcmd) {
1052 state.default_cmd[DebugMode::Casl2 as usize] = None;
1053 } else {
1054 state.default_cmd[DebugMode::Casl2 as usize] = Some(defcmd.to_string());
1055 }
1056 }
1057 if let Some(defcmd) = state.default_cmd[DebugMode::Casl2 as usize].as_ref() {
1058 writeln!(stdout, "デフォルトのデバッガコマンド: {}", defcmd)?;
1059 } else {
1060 writeln!(stdout, "デフォルトのデバッガコマンド: none")?;
1061 }
1062 }
1063 "dump-code" => dump_code(emu, stdout, cmd_and_param.next())?,
1064 "dump-mem" => dump_mem(emu, stdout, cmd_and_param.next())?,
1065 "fill-mem" => fill_mem(emu, stdout, cmd_and_param.next())?,
1066 "find-code" => find_code(emu, stdout, cmd_and_param.next())?,
1067 "find-src" => find_src(emu, stdout, cmd_and_param.next())?,
1068 "find-value" => find_value(emu, stdout, cmd_and_param.next())?,
1069 "help" => show_command_help(cmd_and_param.next(), stdout)?,
1070 "list-files" => list_files(emu, stdout)?,
1071 "mode" => {
1072 if let Some(param) = cmd_and_param.next() {
1073 if "basic".eq_ignore_ascii_case(param) {
1074 state.debug_mode = DebugMode::Basic;
1075 return Ok(REQUEST_CONTINUE);
1076 } else if "casl2".eq_ignore_ascii_case(param) {
1077 writeln!(stdout, "現在CASL2デバッグモードです")?;
1078 } else {
1079 writeln!(stdout, "引数が不正です")?;
1080 }
1081 } else {
1082 writeln!(stdout, "現在CASL2デバッグモードです")?;
1083 }
1084 }
1085 "quit" => {
1086 writeln!(stdout, "テスト実行を中止します")?;
1087 return Ok(REQUEST_QUIT);
1088 }
1089 "remove-breakpoint" => set_breakpoint(emu, stdout, cmd_and_param.next(), false)?,
1090 "reset" => {
1091 writeln!(stdout, "エミュレータをリセットします")?;
1092 writeln!(stdout)?;
1093 return Ok(REQUEST_BREAK);
1094 }
1095 "restart" => {
1096 emu.init_to_start(state.start_point);
1097 state.err = None;
1098 writeln!(stdout, "プログラムをリスタートします")?;
1099 writeln!(stdout)?;
1100 return Ok(REQUEST_CONTINUE);
1101 }
1102 "run" => {
1103 if let Some(s) = cmd_and_param.next() {
1104 match s.parse::<u64>() {
1105 Ok(v) if v > 0 => state.run_mode = RunMode::GoToBreakPoint(v),
1106 _ => {
1107 writeln!(stdout, "引数が不正です")?;
1108 continue;
1109 }
1110 }
1111 } else {
1112 state.run_mode = RunMode::GoToBreakPoint(DEFAULT_STEP_LIMIT);
1113 }
1114 return Ok(0);
1115 }
1116 "set-breakpoint" => set_breakpoint(emu, stdout, cmd_and_param.next(), true)?,
1117 "set-by-file" => set_by_file(emu, stdout, state, cmd_and_param.next(), is_basic_mode)?,
1118 "set-label" => set_label(emu, stdout, cmd_and_param.next())?,
1119 "set-mem" => set_mem(emu, stdout, cmd_and_param.next())?,
1120 "set-reg" => {
1121 let msg = set_reg(emu, cmd_and_param.next());
1122 writeln!(stdout, "{}", msg)?;
1123 }
1124 "set-start" => {
1125 if let Some(s) = cmd_and_param.next() {
1126 let s = s.to_ascii_uppercase();
1127 let mut tokenizer = casl2::Tokenizer::new(s.as_str());
1128 match tokenizer.extended_label() {
1129 Ok(Some(lb)) => {
1130 if !tokenizer.rest().is_empty() {
1131 writeln!(stdout, "引数が不正です")?;
1132 } else {
1133 match lb.get_address(emu) {
1134 Ok(adr) => {
1135 state.start_point = Some(adr);
1136 writeln!(stdout, "スタートポイントを{}に設定しました(次のrestartから有効です)", lb)?;
1137 }
1138 Err(msg) => writeln!(stdout, "{}", msg)?,
1139 }
1140 }
1141 }
1142 Ok(_) => writeln!(stdout, "引数が不正です")?,
1143 Err(msg) => writeln!(stdout, "{}", msg)?,
1144 }
1145 } else {
1146 state.start_point = None;
1147 let name = emu.start_point.as_ref().unwrap();
1148 writeln!(
1149 stdout,
1150 "スタートポイントを{}に戻しました(次のrestartから有効です)",
1151 name
1152 )?;
1153 }
1154 }
1155 "show-labels" => show_labels(emu, stdout, cmd_and_param.next())?,
1156 "show-mem" => show_mem(emu, stdout, cmd_and_param.next())?,
1157 "show-mem-stat" => show_mem_stat(emu, stdout, cmd_and_param.next())?,
1158 "show-reg" => show_reg(emu, stdout)?,
1159 "show-src" => show_src(emu, stdout, cmd_and_param.next())?,
1160 "show-state" => show_state(emu, stdout, state)?,
1161 "show-var" => show_var(emu, stdout, cmd_and_param.next())?,
1162 "skip" => {
1163 if let Some(s) = cmd_and_param.next() {
1164 match s.parse::<u64>() {
1165 Ok(v) if v > 0 => state.run_mode = RunMode::SkipSubroutine(v),
1166 _ => {
1167 writeln!(stdout, "引数が不正です")?;
1168 continue;
1169 }
1170 }
1171 } else {
1172 state.run_mode = RunMode::SkipSubroutine(DEFAULT_STEP_LIMIT);
1173 }
1174 return Ok(0);
1175 }
1176 "step" | "s" => {
1177 if let Some(s) = cmd_and_param.next() {
1178 match s.parse::<u64>() {
1179 Ok(v) if v > 0 => state.run_mode = RunMode::Step(v),
1180 _ => {
1181 writeln!(stdout, "引数が不正です")?;
1182 continue;
1183 }
1184 }
1185 } else {
1186 state.run_mode = RunMode::Step(1);
1187 }
1188 return Ok(0);
1189 }
1190 "write-code" => write_code(emu, stdout, cmd_and_param.next())?,
1191 _ => {
1192 writeln!(stdout, "コマンドが正しくありません")?;
1193 }
1194 }
1195 }
1196}
1197
1198fn show_var_for_basic<W: Write>(
1202 emu: &Emulator,
1203 stdout: &mut W,
1204 param: Option<&str>,
1205) -> io::Result<()> {
1206 let (pg, var_list) = match param {
1207 None => (emu.get_current_program(), None),
1208 Some(param) => {
1209 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
1210 let label = iter.next().unwrap();
1211 let pg = if label == "." {
1212 emu.get_current_program()
1213 } else {
1214 emu.program_list
1215 .iter()
1216 .find(|(_, x, _)| x.eq_ignore_ascii_case(label))
1217 };
1218 match iter.next() {
1219 None => (pg, None),
1220 Some(param) => {
1221 let list = param.split(',').map(|s| s.trim()).collect::<Vec<_>>();
1222 (pg, Some(list))
1223 }
1224 }
1225 }
1226 };
1227
1228 let (file, pg_label, stmt) = match pg {
1229 Some(pg) => pg,
1230 None => {
1231 writeln!(stdout, "BASICコードの情報が見つかりませんでした")?;
1232 return Ok(());
1233 }
1234 };
1235
1236 let label_set = match emu.basic_info.get(file) {
1237 Some((_, info)) => &info.label_set,
1238 None => {
1239 writeln!(stdout, "BASICコードの情報が見つかりませんでした")?;
1240 return Ok(());
1241 }
1242 };
1243
1244 let is_current = !stmt.is_empty() && {
1245 let &(fp, _) = stmt.first().unwrap();
1246 let &(lp, _) = stmt.last().unwrap();
1247 (fp..=lp).contains(&emu.program_register)
1248 };
1249
1250 let is_running = !stmt.is_empty() && {
1251 let &(fp, _) = stmt.first().unwrap();
1252 let &(lp, _) = stmt.last().unwrap();
1253 emu.program_stack
1254 .iter()
1255 .any(|(_, ret)| (fp..=lp).contains(ret))
1256 };
1257
1258 writeln!(stdout, "{}の変数", pg_label)?;
1259
1260 if !(is_running || is_current) || (is_current && matches!(emu.basic_step, Some(0) | None)) {
1261 return print_var_def(stdout, label_set, var_list);
1262 }
1263
1264 use parser::VarType as V;
1265
1266 if let Some(list) = var_list {
1267 for name in list {
1268 if let Some((label, arg)) = label_set.argument_labels.get(name) {
1269 match arg.var_type {
1270 V::Boolean | V::Integer => write!(stdout, " ByVal {:<18} = ", name)?,
1271 V::RefBoolean | V::RefInteger => write!(stdout, " ByRef {:<18} = ", name)?,
1272 _ => unreachable!("BUG"),
1273 }
1274 match emu.resolve_label(pg_label, label) {
1275 Some((pos, None, V::Boolean)) => match emu.mem[pos] {
1276 0x0000 => writeln!(stdout, "False")?,
1277 0xFFFF => writeln!(stdout, "True")?,
1278 _ => writeln!(stdout, "????")?,
1279 },
1280 Some((pos, None, V::Integer)) => writeln!(stdout, "{}", emu.mem[pos] as i16)?,
1281 _ => writeln!(stdout, "(データ破損)")?,
1282 }
1283 } else if let Some((label, arg)) = label_set.str_argument_labels.get(name) {
1284 match arg.var_type {
1285 V::String => write!(stdout, " ByVal {:<18} = ", name)?,
1286 V::RefString => write!(stdout, " ByRef {:<18} = ", name)?,
1287 _ => unreachable!("BUG"),
1288 }
1289 match emu.resolve_label(pg_label, label) {
1290 Some((len, Some(pos), V::String)) => {
1291 let len = emu.mem[len] as usize;
1292 let s = format!(
1293 r#""{}""#,
1294 emu.mem[pos..pos + len.min(256)]
1295 .iter()
1296 .map(|x| jis_x_201::convert_to_char(*x as u8, true))
1297 .collect::<String>()
1298 .replace('"', r#""""#)
1299 );
1300 if len <= 256 {
1301 writeln!(stdout, "({:>3}) {}", len, s)?;
1302 } else {
1303 let s = s.chars().take(30).collect::<String>();
1304 writeln!(stdout, "({:>3}) {} (データ破損)", len, s)?;
1305 }
1306 if (1..=256).contains(&len) {
1307 let t = emu.mem[pos..pos + len.min(256)]
1308 .iter()
1309 .map(|v| format!("{}", *v as i16))
1310 .collect::<Vec<_>>()
1311 .join(", ");
1312 writeln!(stdout, " {:5} {:18} = {}", "", "", t)?;
1313 }
1314 }
1315 _ => writeln!(stdout, "(データ破損)")?,
1316 }
1317 } else if let Some((label, arg)) = label_set.arr_argument_labels.get(name) {
1318 match arg.var_type {
1319 V::ArrayOfBoolean(size) | V::ArrayOfInteger(size) => {
1320 write!(stdout, " ByVal {:<18} = ", format!("{}({})", name, size))?
1321 }
1322 V::RefArrayOfBoolean(size) | V::RefArrayOfInteger(size) => {
1323 write!(stdout, " ByRef {:<18} = ", format!("{}({})", name, size))?
1324 }
1325 _ => unreachable!("BUG"),
1326 }
1327 match emu.resolve_label(pg_label, label) {
1328 Some((pos, None, V::ArrayOfBoolean(size))) => {
1329 let s = emu.mem[pos..pos + size]
1330 .iter()
1331 .map(|v| match *v {
1332 0x0000 => "False",
1333 0xFFFF => "True ",
1334 _ => "???? ",
1335 })
1336 .collect::<Vec<_>>()
1337 .join(", ");
1338 writeln!(stdout, "{}", s)?;
1339 }
1340 Some((pos, None, V::ArrayOfInteger(size))) => {
1341 let s = emu.mem[pos..pos + size]
1342 .iter()
1343 .map(|v| format!("{}", *v as i16))
1344 .collect::<Vec<_>>()
1345 .join(", ");
1346 writeln!(stdout, "{}", s)?;
1347 }
1348 _ => writeln!(stdout, "(データ破損)")?,
1349 }
1350 } else if let Some(label) = label_set.bool_var_labels.get(name) {
1351 write!(stdout, " Dim {:<18} = ", name)?;
1352 match emu.resolve_label(pg_label, label) {
1353 Some((pos, None, V::Boolean)) => match emu.mem[pos] {
1354 0x0000 => writeln!(stdout, "False")?,
1355 0xFFFF => writeln!(stdout, "True")?,
1356 _ => writeln!(stdout, "????")?,
1357 },
1358 _ => writeln!(stdout, "(データ破損)")?,
1359 }
1360 } else if let Some(label) = label_set.int_var_labels.get(name) {
1361 write!(stdout, " Dim {:<18} = ", name)?;
1362 match emu.resolve_label(pg_label, label) {
1363 Some((pos, None, V::Integer)) => writeln!(stdout, "{}", emu.mem[pos] as i16)?,
1364 _ => writeln!(stdout, "(データ破損)")?,
1365 }
1366 } else if let Some(label) = label_set.str_var_labels.get(name) {
1367 write!(stdout, " Dim {:<18} = ", name)?;
1368 match emu.resolve_label(pg_label, label) {
1369 Some((len, Some(pos), V::String)) => {
1370 let len = emu.mem[len] as usize;
1371 let s = format!(
1372 r#""{}""#,
1373 emu.mem[pos..pos + len.min(256)]
1374 .iter()
1375 .map(|x| jis_x_201::convert_to_char(*x as u8, true))
1376 .collect::<String>()
1377 .replace('"', r#""""#)
1378 );
1379 if len <= 256 {
1380 writeln!(stdout, "({:>3}) {}", len, s)?;
1381 } else {
1382 let s = s.chars().take(30).collect::<String>();
1383 writeln!(stdout, "({:>3}) {} (データ破損)", len, s)?;
1384 }
1385 if (1..=256).contains(&len) {
1386 let t = emu.mem[pos..pos + len.min(256)]
1387 .iter()
1388 .map(|v| format!("{}", *v as i16))
1389 .collect::<Vec<_>>()
1390 .join(", ");
1391 writeln!(stdout, " {:5} {:18} = {}", "", "", t)?;
1392 }
1393 }
1394 _ => writeln!(stdout, "(データ破損)")?,
1395 }
1396 } else if let Some(label) = label_set.bool_arr_labels.get(name) {
1397 match emu.resolve_label(pg_label, label) {
1398 Some((pos, None, V::ArrayOfBoolean(size))) => {
1399 let s = emu.mem[pos..pos + size]
1400 .iter()
1401 .map(|v| match *v {
1402 0x0000 => "False",
1403 0xFFFF => "True",
1404 _ => "????",
1405 })
1406 .collect::<Vec<_>>()
1407 .join(", ");
1408 writeln!(
1409 stdout,
1410 " Dim {:<18} = {}",
1411 format!("{}({})", name, size),
1412 s
1413 )?;
1414 }
1415 _ => writeln!(stdout, " Dim {:<18} = (データ破損)", "")?,
1416 }
1417 } else if let Some(label) = label_set.int_arr_labels.get(name) {
1418 match emu.resolve_label(pg_label, label) {
1419 Some((pos, None, V::ArrayOfInteger(size))) => {
1420 let s = emu.mem[pos..pos + size]
1421 .iter()
1422 .map(|v| format!("{}", *v as i16))
1423 .collect::<Vec<_>>()
1424 .join(", ");
1425 writeln!(
1426 stdout,
1427 " Dim {:<18} = {}",
1428 format!("{}({})", name, size),
1429 s
1430 )?;
1431 }
1432 _ => writeln!(stdout, " Dim {:<18} = (データ破損)", "")?,
1433 }
1434 } else {
1435 writeln!(stdout, "変数{}は存在しません", name)?;
1436 }
1437 }
1438 return Ok(());
1439 }
1440
1441 let mut omit_name: Option<&String> = None;
1442
1443 for (name, (label, arg)) in label_set.argument_labels.iter().collect::<BTreeMap<_, _>>() {
1444 match arg.var_type {
1445 V::Boolean | V::Integer => write!(stdout, " ByVal {:<18} = ", name)?,
1446 V::RefBoolean | V::RefInteger => write!(stdout, " ByRef {:<18} = ", name)?,
1447 _ => unreachable!("BUG"),
1448 }
1449 match emu.resolve_label(pg_label, label) {
1450 Some((pos, None, V::Boolean)) => match emu.mem[pos] {
1451 0x0000 => writeln!(stdout, "False")?,
1452 0xFFFF => writeln!(stdout, "True")?,
1453 _ => writeln!(stdout, "????")?,
1454 },
1455 Some((pos, None, V::Integer)) => writeln!(stdout, "{}", emu.mem[pos] as i16)?,
1456 _ => writeln!(stdout, "(データ破損)")?,
1457 }
1458 }
1459
1460 for (name, (label, arg)) in label_set
1461 .str_argument_labels
1462 .iter()
1463 .collect::<BTreeMap<_, _>>()
1464 {
1465 match arg.var_type {
1466 V::String => write!(stdout, " ByVal {:<18} = ", name)?,
1467 V::RefString => write!(stdout, " ByRef {:<18} = ", name)?,
1468 _ => unreachable!("BUG"),
1469 }
1470 match emu.resolve_label(pg_label, label) {
1471 Some((len, Some(pos), V::String)) => {
1472 let len = emu.mem[len] as usize;
1473 let s = format!(
1474 r#""{}""#,
1475 emu.mem[pos..pos + len.min(256)]
1476 .iter()
1477 .map(|x| jis_x_201::convert_to_char(*x as u8, true))
1478 .collect::<String>()
1479 .replace('"', r#""""#)
1480 );
1481 if s.chars().count() < 40 {
1482 writeln!(stdout, "({:>3}) {}", len, s)?;
1483 } else if len <= 256 {
1484 let s = s.chars().take(30).collect::<String>();
1485 writeln!(stdout, "({:>3}) {} (省略)", len, s)?;
1486 omit_name = Some(name);
1487 } else {
1488 let s = s.chars().take(30).collect::<String>();
1489 writeln!(stdout, "({:>3}) {} (データ破損)", len, s)?;
1490 }
1491 if (1..=256).contains(&len) {
1492 let t = emu.mem[pos..pos + len.min(256)]
1493 .iter()
1494 .map(|v| format!("{}", *v as i16))
1495 .collect::<Vec<_>>()
1496 .join(", ");
1497 write!(stdout, " {:5} {:18} = ", "", "")?;
1498 if t.chars().count() < 46 {
1499 writeln!(stdout, "{}", t)?;
1500 } else {
1501 let t = t.chars().take(36).collect::<String>();
1502 writeln!(stdout, "{} (省略)", t)?;
1503 omit_name = Some(name);
1504 }
1505 }
1506 }
1507 _ => writeln!(stdout, "(データ破損)")?,
1508 }
1509 }
1510
1511 for (name, (label, arg)) in label_set
1512 .arr_argument_labels
1513 .iter()
1514 .collect::<BTreeMap<_, _>>()
1515 {
1516 match arg.var_type {
1517 V::ArrayOfBoolean(size) | V::ArrayOfInteger(size) => {
1518 write!(stdout, " ByVal {:<18} = ", format!("{}({})", name, size))?
1519 }
1520 V::RefArrayOfBoolean(size) | V::RefArrayOfInteger(size) => {
1521 write!(stdout, " ByRef {:<18} = ", format!("{}({})", name, size))?
1522 }
1523 _ => unreachable!("BUG"),
1524 }
1525 match emu.resolve_label(pg_label, label) {
1526 Some((pos, None, V::ArrayOfBoolean(size))) => {
1527 let s = emu.mem[pos..pos + size]
1528 .iter()
1529 .map(|v| match *v {
1530 0x0000 => "False",
1531 0xFFFF => "True ",
1532 _ => "???? ",
1533 })
1534 .collect::<Vec<_>>()
1535 .join(", ");
1536 if s.chars().count() < 46 {
1537 writeln!(stdout, "{}", s)?;
1538 } else {
1539 let s = s.chars().take(36).collect::<String>();
1540 writeln!(stdout, "{} (省略)", s)?;
1541 omit_name = Some(name);
1542 }
1543 }
1544 Some((pos, None, V::ArrayOfInteger(size))) => {
1545 let s = emu.mem[pos..pos + size]
1546 .iter()
1547 .map(|v| format!("{}", *v as i16))
1548 .collect::<Vec<_>>()
1549 .join(", ");
1550 if s.chars().count() < 46 {
1551 writeln!(stdout, "{}", s)?;
1552 } else {
1553 let s = s.chars().take(36).collect::<String>();
1554 writeln!(stdout, "{} (省略)", s)?;
1555 omit_name = Some(name);
1556 }
1557 }
1558 _ => writeln!(stdout, "(データ破損)")?,
1559 }
1560 }
1561
1562 for (name, label) in label_set.bool_var_labels.iter().collect::<BTreeMap<_, _>>() {
1563 write!(stdout, " Dim {:<18} = ", name)?;
1564 match emu.resolve_label(pg_label, label) {
1565 Some((pos, None, V::Boolean)) => match emu.mem[pos] {
1566 0x0000 => writeln!(stdout, "False")?,
1567 0xFFFF => writeln!(stdout, "True")?,
1568 _ => writeln!(stdout, "????")?,
1569 },
1570 _ => writeln!(stdout, "(データ破損)")?,
1571 }
1572 }
1573
1574 for (name, label) in label_set.int_var_labels.iter().collect::<BTreeMap<_, _>>() {
1575 write!(stdout, " Dim {:<18} = ", name)?;
1576 match emu.resolve_label(pg_label, label) {
1577 Some((pos, None, V::Integer)) => writeln!(stdout, "{}", emu.mem[pos] as i16)?,
1578 _ => writeln!(stdout, "(データ破損)")?,
1579 }
1580 }
1581
1582 for (name, label) in label_set.str_var_labels.iter().collect::<BTreeMap<_, _>>() {
1583 write!(stdout, " Dim {:<18} = ", name)?;
1584 match emu.resolve_label(pg_label, label) {
1585 Some((len, Some(pos), V::String)) => {
1586 let len = emu.mem[len] as usize;
1587 let s = format!(
1588 r#""{}""#,
1589 emu.mem[pos..pos + len.min(256)]
1590 .iter()
1591 .map(|x| jis_x_201::convert_to_char(*x as u8, true))
1592 .collect::<String>()
1593 .replace('"', r#""""#)
1594 );
1595 if s.chars().count() < 40 {
1596 writeln!(stdout, "({:>3}) {}", len, s)?;
1597 } else if len <= 256 {
1598 let s = s.chars().take(30).collect::<String>();
1599 writeln!(stdout, "({:>3}) {} (省略)", len, s)?;
1600 omit_name = Some(name);
1601 } else {
1602 let s = s.chars().take(30).collect::<String>();
1603 writeln!(stdout, "({:>3}) {} (データ破損)", len, s)?;
1604 }
1605 if (1..=256).contains(&len) {
1606 let t = emu.mem[pos..pos + len.min(256)]
1607 .iter()
1608 .map(|v| format!("{}", *v as i16))
1609 .collect::<Vec<_>>()
1610 .join(", ");
1611 write!(stdout, " {:5} {:18} = ", "", "")?;
1612 if t.chars().count() < 46 {
1613 writeln!(stdout, "{}", t)?;
1614 } else {
1615 let t = t.chars().take(36).collect::<String>();
1616 writeln!(stdout, "{} (省略)", t)?;
1617 omit_name = Some(name);
1618 }
1619 }
1620 }
1621 _ => writeln!(stdout, "(データ破損)")?,
1622 }
1623 }
1624
1625 for (name, label) in label_set.bool_arr_labels.iter().collect::<BTreeMap<_, _>>() {
1626 match emu.resolve_label(pg_label, label) {
1627 Some((pos, None, V::ArrayOfBoolean(size))) => {
1628 write!(stdout, " Dim {:<18} = ", format!("{}({})", name, size))?;
1629 let s = emu.mem[pos..pos + size]
1630 .iter()
1631 .map(|v| match *v {
1632 0x0000 => "False",
1633 0xFFFF => "True",
1634 _ => "????",
1635 })
1636 .collect::<Vec<_>>()
1637 .join(", ");
1638 if s.chars().count() < 46 {
1639 writeln!(stdout, "{}", s)?;
1640 } else {
1641 let s = s.chars().take(36).collect::<String>();
1642 writeln!(stdout, "{} (省略)", s)?;
1643 omit_name = Some(name);
1644 }
1645 }
1646 _ => writeln!(stdout, " Dim {:<18} = (データ破損)", "")?,
1647 }
1648 }
1649
1650 for (name, label) in label_set.int_arr_labels.iter().collect::<BTreeMap<_, _>>() {
1651 match emu.resolve_label(pg_label, label) {
1652 Some((pos, None, V::ArrayOfInteger(size))) => {
1653 write!(stdout, " Dim {:<18} = ", format!("{}({})", name, size))?;
1654 let s = emu.mem[pos..pos + size]
1655 .iter()
1656 .map(|v| format!("{}", *v as i16))
1657 .collect::<Vec<_>>()
1658 .join(", ");
1659 if s.chars().count() < 46 {
1660 writeln!(stdout, "{}", s)?;
1661 } else {
1662 let s = s.chars().take(36).collect::<String>();
1663 writeln!(stdout, "{} (省略)", s)?;
1664 omit_name = Some(name);
1665 }
1666 }
1667 _ => writeln!(stdout, " Dim {:<18} = (データ破損)", "")?,
1668 }
1669 }
1670
1671 if let Some(name) = omit_name {
1672 writeln!(stdout)?;
1673 writeln!(
1674 stdout,
1675 "※省略された値を確認するには変数名を指定した実行が必要です"
1676 )?;
1677 writeln!(stdout, " ( 例: show-var {} {} )", pg_label, name)?;
1678 }
1679
1680 Ok(())
1681}
1682
1683fn set_len<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
1687 if matches!(emu.basic_step, Some(0) | None) {
1688 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1689 return Ok(());
1690 }
1691 let (var_name, new_len) = match param {
1692 None => {
1693 writeln!(stdout, "引数が必要です")?;
1694 return Ok(());
1695 }
1696 Some(param) => {
1697 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
1698 let var_name = iter.next().unwrap();
1699 let rest = match iter.next() {
1700 Some(rest) => rest,
1701 None => {
1702 writeln!(stdout, "引数が不正です")?;
1703 return Ok(());
1704 }
1705 };
1706 match rest.parse::<u8>() {
1707 Ok(len) => (var_name, len as u16),
1708 Err(_) => {
1709 writeln!(stdout, "引数が不正です")?;
1710 return Ok(());
1711 }
1712 }
1713 }
1714 };
1715
1716 let target = {
1717 let (file, pg_label, label_set) = match emu.get_current_program() {
1718 None => {
1719 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1720 return Ok(());
1721 }
1722 Some((file, _, _)) => match emu.basic_info.get(file) {
1723 Some((label, info)) => (file, label, &info.label_set),
1724 None => {
1725 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1726 return Ok(());
1727 }
1728 },
1729 };
1730
1731 let target = {
1732 if let Some((label, _)) = label_set.str_argument_labels.get(var_name) {
1733 emu.resolve_label(pg_label, label)
1734 } else if let Some(label) = label_set.str_var_labels.get(var_name) {
1735 emu.resolve_label(pg_label, label)
1736 } else {
1737 None
1738 }
1739 };
1740
1741 match target {
1742 Some(target) => target,
1743 None => {
1744 writeln!(stdout, "{}に文字列変数{}が見つかりません", file, var_name)?;
1745 return Ok(());
1746 }
1747 }
1748 };
1749
1750 if let (len, Some(_), parser::VarType::String) = target {
1751 emu.mem[len] = new_len;
1752 } else {
1753 unreachable!("たぶん");
1754 }
1755
1756 writeln!(stdout, "{}の長さを{}を設定しました", var_name, new_len)?;
1757
1758 Ok(())
1759}
1760
1761fn fill_arr<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
1765 if matches!(emu.basic_step, Some(0) | None) {
1766 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1767 return Ok(());
1768 }
1769 let (var_name, value) = match param {
1770 None => {
1771 writeln!(stdout, "引数が必要です")?;
1772 return Ok(());
1773 }
1774 Some(param) => {
1775 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
1776 let var_name = iter.next().unwrap();
1777 match iter.next() {
1778 Some(value) => (var_name, value),
1779 None => {
1780 writeln!(stdout, "引数が不正です")?;
1781 return Ok(());
1782 }
1783 }
1784 }
1785 };
1786
1787 let target = {
1788 let (file, pg_label, label_set) = match emu.get_current_program() {
1789 None => {
1790 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1791 return Ok(());
1792 }
1793 Some((file, _, _)) => match emu.basic_info.get(file) {
1794 Some((label, info)) => (file, label, &info.label_set),
1795 None => {
1796 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1797 return Ok(());
1798 }
1799 },
1800 };
1801
1802 let target = {
1803 if label_set.argument_labels.contains_key(var_name) {
1804 writeln!(stdout, "{}は要素アクセスできません", var_name)?;
1805 return Ok(());
1806 } else if let Some((label, _)) = label_set.arr_argument_labels.get(var_name) {
1807 emu.resolve_label(pg_label, label)
1808 } else if let Some((label, _)) = label_set.str_argument_labels.get(var_name) {
1809 emu.resolve_label(pg_label, label)
1810 } else if label_set.int_var_labels.contains_key(var_name)
1811 || label_set.bool_var_labels.contains_key(var_name)
1812 {
1813 writeln!(stdout, "{}は要素アクセスできません", var_name)?;
1814 return Ok(());
1815 } else if let Some(label) = label_set
1816 .int_arr_labels
1817 .get(var_name)
1818 .or_else(|| label_set.bool_arr_labels.get(var_name))
1819 {
1820 emu.resolve_label(pg_label, label)
1821 } else if let Some(label) = label_set.str_var_labels.get(var_name) {
1822 emu.resolve_label(pg_label, label)
1823 } else {
1824 None
1825 }
1826 };
1827
1828 match target {
1829 Some(target) => target,
1830 None => {
1831 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
1832 return Ok(());
1833 }
1834 }
1835 };
1836
1837 let value = {
1838 let res = match tokenizer::Tokenizer::new(value.as_bytes()).next() {
1839 Some(res) => res?,
1840 None => {
1841 writeln!(stdout, "引数が不正です")?;
1842 return Ok(());
1843 }
1844 };
1845 match res {
1846 Ok((_, tokens)) => tokens.into_iter().map(|(_, tk)| tk).collect::<Vec<_>>(),
1847 Err(_) => {
1848 writeln!(stdout, "引数が不正です")?;
1849 return Ok(());
1850 }
1851 }
1852 };
1853
1854 use parser::VarType as V;
1855
1856 match target {
1857 (pos, None, V::ArrayOfBoolean(size)) => {
1858 if let Some((vs, s)) = take_basic_bool_values(&value) {
1859 if let [v] = vs.as_slice() {
1860 emu.mem[pos..pos + size].fill(*v);
1861 writeln!(stdout, "{}の内容を{}で埋めました", var_name, s)?;
1862 return Ok(());
1863 }
1864 }
1865 }
1866 (pos, None, V::ArrayOfInteger(size)) => {
1867 if let Some((vs, s)) = take_basic_int_values(&value) {
1868 if let [v] = vs.as_slice() {
1869 emu.mem[pos..pos + size].fill(*v);
1870 writeln!(stdout, "{}の内容を{}で埋めました", var_name, s)?;
1871 return Ok(());
1872 }
1873 }
1874 }
1875 (len, Some(pos), V::String) => {
1876 let size = emu.mem[len] as usize;
1877 if size > 256 {
1878 writeln!(stdout, "{}は壊れています", var_name)?;
1879 return Ok(());
1880 }
1881 if let Some((vs, s)) = take_basic_int_values(&value) {
1882 if let [v] = vs.as_slice() {
1883 emu.mem[pos..pos + size].fill(*v);
1884 writeln!(stdout, "{}の内容を{}で埋めました", var_name, s)?;
1885 return Ok(());
1886 }
1887 }
1888 }
1889 _ => unreachable!("BUG"),
1890 }
1891
1892 writeln!(stdout, "引数が不正です")?;
1893 Ok(())
1894}
1895
1896fn set_elem<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
1900 if matches!(emu.basic_step, Some(0) | None) {
1901 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1902 return Ok(());
1903 }
1904 let (var_name, index, value) = match param {
1905 None => {
1906 writeln!(stdout, "引数が必要です")?;
1907 return Ok(());
1908 }
1909 Some(param) => {
1910 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
1911 let var_name = iter.next().unwrap();
1912 let mut iter = match iter.next() {
1913 Some(rest) => rest.splitn(2, ' ').map(|s| s.trim()),
1914 None => {
1915 writeln!(stdout, "引数が不正です")?;
1916 return Ok(());
1917 }
1918 };
1919 let index = iter.next().unwrap();
1920 match iter.next() {
1921 Some(value) => (var_name, index, value),
1922 None => {
1923 writeln!(stdout, "引数が不正です")?;
1924 return Ok(());
1925 }
1926 }
1927 }
1928 };
1929
1930 let target = {
1931 let (file, pg_label, label_set) = match emu.get_current_program() {
1932 None => {
1933 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1934 return Ok(());
1935 }
1936 Some((file, _, _)) => match emu.basic_info.get(file) {
1937 Some((label, info)) => (file, label, &info.label_set),
1938 None => {
1939 writeln!(stdout, "現在地点での変数の設定は行えません")?;
1940 return Ok(());
1941 }
1942 },
1943 };
1944
1945 let target = {
1946 if label_set.argument_labels.contains_key(var_name) {
1947 writeln!(stdout, "{}は要素アクセスできません", var_name)?;
1948 return Ok(());
1949 } else if let Some((label, _)) = label_set.arr_argument_labels.get(var_name) {
1950 emu.resolve_label(pg_label, label)
1951 } else if let Some((label, _)) = label_set.str_argument_labels.get(var_name) {
1952 emu.resolve_label(pg_label, label)
1953 } else if label_set.int_var_labels.contains_key(var_name)
1954 || label_set.bool_var_labels.contains_key(var_name)
1955 {
1956 writeln!(stdout, "{}は要素アクセスできません", var_name)?;
1957 return Ok(());
1958 } else if let Some(label) = label_set
1959 .int_arr_labels
1960 .get(var_name)
1961 .or_else(|| label_set.bool_arr_labels.get(var_name))
1962 {
1963 emu.resolve_label(pg_label, label)
1964 } else if let Some(label) = label_set.str_var_labels.get(var_name) {
1965 emu.resolve_label(pg_label, label)
1966 } else {
1967 None
1968 }
1969 };
1970
1971 match target {
1972 Some(target) => target,
1973 None => {
1974 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
1975 return Ok(());
1976 }
1977 }
1978 };
1979
1980 let index = match index.parse::<u16>() {
1981 Ok(index) => index as usize,
1982 Err(_) => {
1983 writeln!(stdout, "Indexが不正です")?;
1984 return Ok(());
1985 }
1986 };
1987
1988 let value = {
1989 let res = match tokenizer::Tokenizer::new(value.as_bytes()).next() {
1990 Some(res) => res?,
1991 None => {
1992 writeln!(stdout, "引数が不正です")?;
1993 return Ok(());
1994 }
1995 };
1996 match res {
1997 Ok((_, tokens)) => tokens.into_iter().map(|(_, tk)| tk).collect::<Vec<_>>(),
1998 Err(_) => {
1999 writeln!(stdout, "引数が不正です")?;
2000 return Ok(());
2001 }
2002 }
2003 };
2004
2005 use parser::VarType as V;
2006
2007 match target {
2008 (pos, None, V::ArrayOfBoolean(size)) => {
2009 if index >= size {
2010 writeln!(stdout, "Indexが不正です")?;
2011 return Ok(());
2012 }
2013 if let Some((vs, s)) = take_basic_bool_values(&value) {
2014 if let [v] = vs.as_slice() {
2015 emu.mem[pos + index] = *v;
2016 writeln!(stdout, "{}({})に{}を設定しました", var_name, index, s)?;
2017 return Ok(());
2018 }
2019 }
2020 }
2021 (pos, None, V::ArrayOfInteger(size)) => {
2022 if index >= size {
2023 writeln!(stdout, "Indexが不正です")?;
2024 return Ok(());
2025 }
2026 if let Some((vs, s)) = take_basic_int_values(&value) {
2027 if let [v] = vs.as_slice() {
2028 emu.mem[pos + index] = *v;
2029 writeln!(stdout, "{}({})に{}を設定しました", var_name, index, s)?;
2030 return Ok(());
2031 }
2032 }
2033 }
2034 (len, Some(pos), V::String) => {
2035 let size = emu.mem[len] as usize;
2036 if size > 256 {
2037 writeln!(stdout, "{}は壊れています", var_name)?;
2038 return Ok(());
2039 }
2040 if index >= size {
2041 writeln!(stdout, "Indexが不正です")?;
2042 return Ok(());
2043 }
2044 if let Some((vs, s)) = take_basic_int_values(&value) {
2045 if let [v] = vs.as_slice() {
2046 emu.mem[pos + index] = *v;
2047 writeln!(stdout, "{}({})に{}を設定しました", var_name, index, s)?;
2048 return Ok(());
2049 }
2050 }
2051 }
2052 _ => unreachable!("BUG"),
2053 }
2054
2055 writeln!(stdout, "引数が不正です")?;
2056 Ok(())
2057}
2058
2059fn set_var<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
2063 if emu.basic_step.is_none() {
2064 writeln!(stdout, "現在地点での変数の設定は行えません")?;
2065 return Ok(());
2066 }
2067 let (var_name, values) = match param {
2068 None => {
2069 writeln!(stdout, "引数が必要です")?;
2070 return Ok(());
2071 }
2072 Some(param) => {
2073 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
2074 let var_name = iter.next().unwrap();
2075 let values = match iter.next() {
2076 None => {
2077 writeln!(stdout, "引数が不正です")?;
2078 return Ok(());
2079 }
2080 Some(values) => {
2081 let res = match tokenizer::Tokenizer::new(values.as_bytes()).next() {
2082 Some(res) => res?,
2083 None => {
2084 writeln!(stdout, "引数が不正です")?;
2085 return Ok(());
2086 }
2087 };
2088 match res {
2089 Ok((_, tokens)) => tokens.into_iter().map(|(_, tk)| tk).collect::<Vec<_>>(),
2090 Err(_) => {
2091 writeln!(stdout, "引数が不正です")?;
2092 return Ok(());
2093 }
2094 }
2095 }
2096 };
2097 (var_name, values)
2098 }
2099 };
2100
2101 let target = {
2102 let (file, pg_label, label_set) = match emu.get_current_program() {
2103 None => {
2104 writeln!(stdout, "現在地点での変数の設定は行えません")?;
2105 return Ok(());
2106 }
2107 Some((file, _, _)) => match emu.basic_info.get(file) {
2108 Some((label, info)) => (file, label, &info.label_set),
2109 None => {
2110 writeln!(stdout, "現在地点での変数の設定は行えません")?;
2111 return Ok(());
2112 }
2113 },
2114 };
2115
2116 if let Some((label, arg)) = label_set.argument_labels.get(var_name) {
2117 if matches!(emu.basic_step, Some(0)) {
2118 Err(arg.clone())
2119 } else {
2120 match emu.resolve_label(pg_label, label) {
2121 Some(res) => Ok(res),
2122 None => {
2123 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2124 return Ok(());
2125 }
2126 }
2127 }
2128 } else if let Some((label, arg)) = label_set.arr_argument_labels.get(var_name) {
2129 if matches!(emu.basic_step, Some(0)) {
2130 Err(arg.clone())
2131 } else {
2132 match emu.resolve_label(pg_label, label) {
2133 Some(res) => Ok(res),
2134 None => {
2135 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2136 return Ok(());
2137 }
2138 }
2139 }
2140 } else if let Some((label, arg)) = label_set.str_argument_labels.get(var_name) {
2141 if matches!(emu.basic_step, Some(0)) {
2142 Err(arg.clone())
2143 } else {
2144 match emu.resolve_label(pg_label, label) {
2145 Some(res) => Ok(res),
2146 None => {
2147 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2148 return Ok(());
2149 }
2150 }
2151 }
2152 } else if matches!(emu.basic_step, Some(0)) {
2153 writeln!(stdout, "現在地点での変数の設定は行えません")?;
2154 return Ok(());
2155 } else if let Some(label) = label_set
2156 .int_var_labels
2157 .get(var_name)
2158 .or_else(|| label_set.bool_var_labels.get(var_name))
2159 {
2160 match emu.resolve_label(pg_label, label) {
2161 Some(res) => Ok(res),
2162 None => {
2163 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2164 return Ok(());
2165 }
2166 }
2167 } else if let Some(label) = label_set
2168 .int_arr_labels
2169 .get(var_name)
2170 .or_else(|| label_set.bool_arr_labels.get(var_name))
2171 {
2172 match emu.resolve_label(pg_label, label) {
2173 Some(res) => Ok(res),
2174 None => {
2175 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2176 return Ok(());
2177 }
2178 }
2179 } else if let Some(label) = label_set.str_var_labels.get(var_name) {
2180 match emu.resolve_label(pg_label, label) {
2181 Some(res) => Ok(res),
2182 None => {
2183 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2184 return Ok(());
2185 }
2186 }
2187 } else {
2188 writeln!(stdout, "{}に変数{}が見つかりません", file, var_name)?;
2189 return Ok(());
2190 }
2191 };
2192
2193 use parser::VarType as V;
2194 use tokenizer::Token as T;
2195
2196 match target {
2197 Ok((pos, None, V::Boolean)) => {
2198 if let Some((vs, s)) = take_basic_bool_values(&values) {
2199 if let [v] = vs.as_slice() {
2200 emu.mem[pos] = *v;
2201 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2202 return Ok(());
2203 }
2204 }
2205 }
2206 Ok((pos, None, V::Integer)) => {
2207 if let Some((vs, s)) = take_basic_int_values(&values) {
2208 if let [v] = vs.as_slice() {
2209 emu.mem[pos] = *v;
2210 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2211 return Ok(());
2212 }
2213 }
2214 }
2215 Ok((pos, None, V::ArrayOfBoolean(size))) => {
2216 if let Some((vs, s)) = take_basic_bool_values(&values) {
2217 if vs.len() == size {
2218 for (i, v) in vs.iter().enumerate() {
2219 emu.mem[pos + i] = *v;
2220 }
2221 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2222 return Ok(());
2223 }
2224 }
2225 }
2226 Ok((pos, None, V::ArrayOfInteger(size))) => {
2227 if let Some((vs, s)) = take_basic_int_values(&values) {
2228 if vs.len() == size {
2229 for (i, v) in vs.iter().enumerate() {
2230 emu.mem[pos + i] = *v;
2231 }
2232 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2233 return Ok(());
2234 }
2235 }
2236 }
2237 Ok((len, Some(pos), V::String)) => {
2238 if let [T::String(s)] = values.as_slice() {
2239 let s = jis_x_201::convert_kana_wide_full_to_half(s)
2240 .chars()
2241 .take(256)
2242 .collect::<String>();
2243 emu.mem[len] = s.chars().count() as u16;
2244 for (i, ch) in s.chars().enumerate() {
2245 emu.mem[pos + i] = jis_x_201::convert_from_char(ch) as u16;
2246 }
2247 writeln!(
2248 stdout,
2249 r#"{}に"{}"を設定しました"#,
2250 var_name,
2251 s.replace('"', r#""""#)
2252 )?;
2253 return Ok(());
2254 }
2255 if let Some((vs, s)) = take_basic_int_values(&values) {
2256 if vs.len() <= 256 {
2257 emu.mem[len] = vs.len() as u16;
2258 for (i, v) in vs.iter().enumerate() {
2259 emu.mem[pos + i] = *v;
2260 }
2261 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2262 return Ok(());
2263 }
2264 }
2265 }
2266 Ok(_) => unreachable!("BUG"),
2267 Err(arg) => match arg.var_type {
2268 V::Boolean => {
2269 if let Some((vs, s)) = take_basic_bool_values(&values) {
2270 if let [v] = vs.as_slice() {
2271 emu.general_registers[arg.register1 as usize] = *v;
2272 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2273 return Ok(());
2274 }
2275 }
2276 }
2277 V::Integer => {
2278 if let Some((vs, s)) = take_basic_int_values(&values) {
2279 if let [v] = vs.as_slice() {
2280 emu.general_registers[arg.register1 as usize] = *v;
2281 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2282 return Ok(());
2283 }
2284 }
2285 }
2286 V::String => {
2287 if let [T::String(s)] = values.as_slice() {
2288 if s.is_empty() {
2289 emu.general_registers[arg.register1 as usize] = 0;
2290 emu.general_registers[arg.register2.unwrap() as usize] = 0;
2291 writeln!(stdout, r#"{}に""を設定しました"#, var_name)?;
2292 } else {
2293 match emu.make_literal_str(s) {
2294 Err(s) => writeln!(stdout, "{}", s)?,
2295 Ok((pos, s)) => {
2296 emu.general_registers[arg.register1 as usize] =
2297 s.chars().count().min(256) as u16;
2298 emu.general_registers[arg.register2.unwrap() as usize] = pos as u16;
2299 writeln!(
2300 stdout,
2301 r#"{}に"{}"を設定しました"#,
2302 var_name,
2303 s.replace('"', r#""""#)
2304 )?;
2305 }
2306 }
2307 }
2308 return Ok(());
2309 }
2310 if let Some((vs, s)) = take_basic_int_values(&values) {
2311 if vs.len() <= 256 {
2312 if !emu.enough_remain(vs.len()) {
2313 writeln!(stdout, "メモリ不足でリテラル領域を確保できませんでした")?;
2314 return Ok(());
2315 }
2316 let pos = emu.compile_pos;
2317 emu.compile_pos += vs.len();
2318 emu.general_registers[arg.register1 as usize] = vs.len() as u16;
2319 emu.general_registers[arg.register2.unwrap() as usize] = pos as u16;
2320 for (i, v) in vs.iter().enumerate() {
2321 emu.mem[pos + i] = *v;
2322 }
2323 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2324 return Ok(());
2325 }
2326 }
2327 }
2328 V::ArrayOfBoolean(size) | V::RefArrayOfBoolean(size) => {
2329 if !emu.enough_remain(size) {
2330 writeln!(stdout, "メモリ不足でリテラル領域を確保できません")?;
2331 return Ok(());
2332 }
2333 if let Some((vs, s)) = take_basic_bool_values(&values) {
2334 if vs.len() == size {
2335 let pos = emu.compile_pos;
2336 emu.compile_pos += vs.len();
2337 emu.general_registers[arg.register1 as usize] = pos as u16;
2338 for (i, v) in vs.iter().enumerate() {
2339 emu.mem[pos + i] = *v;
2340 }
2341 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2342 return Ok(());
2343 }
2344 }
2345 }
2346 V::ArrayOfInteger(size) | V::RefArrayOfInteger(size) => {
2347 if !emu.enough_remain(size) {
2348 writeln!(stdout, "メモリ不足でリテラル領域を確保できません")?;
2349 return Ok(());
2350 }
2351 if let Some((vs, s)) = take_basic_int_values(&values) {
2352 if vs.len() == size {
2353 let pos = emu.compile_pos;
2354 emu.compile_pos += vs.len();
2355 emu.general_registers[arg.register1 as usize] = pos as u16;
2356 for (i, v) in vs.iter().enumerate() {
2357 emu.mem[pos + i] = *v;
2358 }
2359 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2360 return Ok(());
2361 }
2362 }
2363 }
2364 V::RefBoolean => {
2365 if !emu.enough_remain(1) {
2366 writeln!(stdout, "メモリ不足でリテラル領域を確保できません")?;
2367 return Ok(());
2368 }
2369 if let Some((vs, s)) = take_basic_bool_values(&values) {
2370 if let [v] = vs.as_slice() {
2371 let pos = emu.compile_pos;
2372 emu.compile_pos += 1;
2373 emu.general_registers[arg.register1 as usize] = pos as u16;
2374 emu.mem[pos] = *v;
2375 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2376 return Ok(());
2377 }
2378 }
2379 }
2380 V::RefInteger => {
2381 if !emu.enough_remain(1) {
2382 writeln!(stdout, "メモリ不足でリテラル領域を確保できません")?;
2383 return Ok(());
2384 }
2385 if let Some((vs, s)) = take_basic_int_values(&values) {
2386 if let [v] = vs.as_slice() {
2387 let pos = emu.compile_pos;
2388 emu.compile_pos += 1;
2389 emu.general_registers[arg.register1 as usize] = pos as u16;
2390 emu.mem[pos] = *v;
2391 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2392 return Ok(());
2393 }
2394 }
2395 }
2396 V::RefString => {
2397 if !emu.enough_remain(257) {
2398 writeln!(stdout, "メモリ不足でリテラル領域を確保できませんでした")?;
2399 return Ok(());
2400 }
2401 if let [T::String(s)] = values.as_slice() {
2402 let t = jis_x_201::convert_kana_wide_full_to_half(s)
2403 .chars()
2404 .take(256)
2405 .collect::<String>();
2406 let pos = emu.compile_pos;
2407 emu.compile_pos += 257;
2408 emu.general_registers[arg.register1 as usize] = pos as u16;
2409 emu.general_registers[arg.register2.unwrap() as usize] = pos as u16 + 1;
2410 emu.mem[pos] = t.chars().count() as u16;
2411 for (i, ch) in t.chars().enumerate() {
2412 emu.mem[pos + i + 1] = jis_x_201::convert_from_char(ch) as u16;
2413 }
2414 writeln!(
2415 stdout,
2416 r#"{}に"{}"を設定しました"#,
2417 var_name,
2418 t.replace('"', r#""""#)
2419 )?;
2420 return Ok(());
2421 }
2422 if let Some((vs, s)) = take_basic_int_values(&values) {
2423 if vs.len() <= 256 {
2424 let pos = emu.compile_pos;
2425 emu.compile_pos += 257;
2426 emu.general_registers[arg.register1 as usize] = pos as u16;
2427 emu.general_registers[arg.register2.unwrap() as usize] = pos as u16 + 1;
2428 emu.mem[pos] = vs.len() as u16;
2429 for (i, v) in vs.iter().enumerate() {
2430 emu.mem[pos + i + 1] = *v;
2431 }
2432 writeln!(stdout, "{}に{}を設定しました", var_name, s)?;
2433 return Ok(());
2434 }
2435 }
2436 }
2437 },
2438 }
2439
2440 writeln!(stdout, "引数が不正です")?;
2441 Ok(())
2442}
2443
2444fn set_breakpoint_for_basic<W: Write>(
2449 emu: &mut Emulator,
2450 stdout: &mut W,
2451 param: Option<&str>,
2452 value: bool,
2453) -> io::Result<()> {
2454 let (label, params) = match param {
2455 None => {
2456 writeln!(stdout, "引数が必要です")?;
2457 return Ok(());
2458 }
2459 Some(param) => {
2460 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
2461 let label = iter.next().unwrap();
2462 match iter.next() {
2463 None => {
2464 writeln!(stdout, "引数が不正です")?;
2465 return Ok(());
2466 }
2467 Some(param) => (label, param),
2468 }
2469 }
2470 };
2471
2472 let (bps, points) = {
2473 let pg = if label == "." {
2474 emu.get_current_program()
2475 } else {
2476 emu.program_list
2477 .iter()
2478 .find(|(_, x, _)| x.eq_ignore_ascii_case(label))
2479 };
2480
2481 let (file, label, stmt) = match pg {
2482 Some((file, label, stmt)) => (file, label, stmt),
2483 None => {
2484 writeln!(stdout, "BASICコードの情報が見つかりませんでした")?;
2485 return Ok(());
2486 }
2487 };
2488
2489 let info = match emu.basic_info.get(file) {
2490 Some((_, info)) => info,
2491 None => {
2492 writeln!(stdout, "{}のBASICコードの情報が見つかりませんでした", label)?;
2493 return Ok(());
2494 }
2495 };
2496
2497 let mut points = vec![];
2498 for p in params.split(',') {
2499 match p.trim().parse::<usize>() {
2500 Ok(v) if v < info.status_hint.len() => points.push(v),
2501 _ => {
2502 writeln!(stdout, "引数が不正です")?;
2503 return Ok(());
2504 }
2505 }
2506 }
2507
2508 let bps = stmt
2509 .iter()
2510 .filter_map(|(pos, stmt)| match stmt {
2511 casl2::Statement::Code {
2512 command: casl2::Command::DebugBasicStep { id },
2513 ..
2514 } => Some((*pos, *id)),
2515 _ => None,
2516 })
2517 .collect::<Vec<_>>();
2518
2519 (bps, points)
2520 };
2521
2522 for p in points {
2523 if let Some((pos, _)) = bps.iter().find(|(_, id)| *id == p) {
2524 emu.break_points[*pos] = value;
2525 }
2526 }
2527
2528 let label = if label == "." {
2529 emu.get_current_program()
2530 .map(|(_, label, _)| label.as_str())
2531 .unwrap()
2532 } else {
2533 label
2534 };
2535
2536 if value {
2537 writeln!(
2538 stdout,
2539 "{}の{}にブレークポイントを設定しました",
2540 label.to_ascii_uppercase(),
2541 params
2542 )?;
2543 } else {
2544 writeln!(
2545 stdout,
2546 "{}の{}からブレークポイントを解除しました",
2547 label.to_ascii_uppercase(),
2548 params
2549 )?;
2550 }
2551
2552 Ok(())
2553}
2554
2555fn show_execute_stat<W: Write>(
2559 emu: &Emulator,
2560 stdout: &mut W,
2561 param: Option<&str>,
2562) -> io::Result<()> {
2563 let (file, label, stmt) = match param {
2564 Some(".") => match emu.get_current_program() {
2565 Some(pg) => pg,
2566 None => {
2567 writeln!(stdout, "BASICコードの情報が見つかりませんでした")?;
2568 return Ok(());
2569 }
2570 },
2571 Some(label) => match emu
2572 .program_list
2573 .iter()
2574 .find(|(_, x, _)| x.eq_ignore_ascii_case(label))
2575 {
2576 Some(pg) => pg,
2577 None => {
2578 writeln!(stdout, "{}のBASICコードの情報が見つかりませんでした", label)?;
2579 return Ok(());
2580 }
2581 },
2582 None => match emu.get_current_program() {
2583 Some(pg) => pg,
2584 None => {
2585 writeln!(stdout, "引数が必要です")?;
2586 return Ok(());
2587 }
2588 },
2589 };
2590
2591 let info = match emu.basic_info.get(file) {
2592 Some((_, info)) => info,
2593 None => {
2594 writeln!(stdout, "{}のBASICコードの情報が見つかりませんでした", label)?;
2595 return Ok(());
2596 }
2597 };
2598
2599 writeln!(stdout, "{}の実行情報", label)?;
2600
2601 let bps = stmt
2602 .iter()
2603 .filter_map(|(pos, stmt)| match stmt {
2604 casl2::Statement::Code {
2605 command: casl2::Command::DebugBasicStep { id },
2606 ..
2607 } => Some((*pos, *id)),
2608 _ => None,
2609 })
2610 .collect::<Vec<_>>();
2611
2612 writeln!(stdout, "到達回数")?;
2613 for (i, (n, hint, _)) in info.status_hint.iter().enumerate() {
2614 let (bp, cnt) = match bps.iter().find(|(_, id)| *id == i) {
2615 Some((pos, id)) => {
2616 assert_eq!(*id, i);
2617 let cnt = emu.execute_count[*pos];
2618 if emu.break_points[*pos] {
2619 ("*", cnt)
2620 } else {
2621 (" ", cnt)
2622 }
2623 }
2624 _ => (" ", 0),
2625 };
2626 writeln!(
2627 stdout,
2628 "{0:8} {1:5}: {2} {3:4$}{5}",
2629 cnt,
2630 i,
2631 bp,
2632 "",
2633 n * 2,
2634 hint
2635 )?;
2636 }
2637
2638 Ok(())
2639}
2640
2641fn show_src_basic<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
2645 let (file, label, stmt) = match param {
2646 Some(".") => match emu.get_current_program() {
2647 Some(pg) => pg,
2648 None => {
2649 writeln!(stdout, "BASICコードの情報が見つかりませんでした")?;
2650 return Ok(());
2651 }
2652 },
2653 Some(label) => match emu
2654 .program_list
2655 .iter()
2656 .find(|(_, x, _)| x.eq_ignore_ascii_case(label))
2657 {
2658 Some(pg) => pg,
2659 None => {
2660 writeln!(stdout, "{}のBASICコードの情報が見つかりませんでした", label)?;
2661 return Ok(());
2662 }
2663 },
2664 None => match emu.get_current_program() {
2665 Some(pg) => pg,
2666 None => {
2667 writeln!(stdout, "引数が必要です")?;
2668 return Ok(());
2669 }
2670 },
2671 };
2672
2673 let info = match emu.basic_info.get(file) {
2674 Some((_, info)) => info,
2675 None => {
2676 writeln!(stdout, "{}のBASICコードの情報が見つかりませんでした", label)?;
2677 return Ok(());
2678 }
2679 };
2680
2681 writeln!(stdout, "{}のBASICコード", label)?;
2682
2683 print_var_def(stdout, &info.label_set, None)?;
2684
2685 let bps = stmt
2686 .iter()
2687 .filter_map(|(pos, stmt)| match stmt {
2688 casl2::Statement::Code {
2689 command: casl2::Command::DebugBasicStep { id },
2690 ..
2691 } => Some((*pos, *id)),
2692 _ => None,
2693 })
2694 .collect::<Vec<_>>();
2695
2696 for (i, (n, hint, _)) in info.status_hint.iter().enumerate() {
2697 let bp = match bps.iter().find(|(_, id)| *id == i) {
2698 Some((pos, id)) if emu.break_points[*pos] => {
2699 assert_eq!(*id, i);
2700 "*"
2701 }
2702 _ => " ",
2703 };
2704 writeln!(stdout, "{0:5}: {1} {2:3$}{4}", i, bp, "", n * 2, hint)?;
2705 }
2706
2707 Ok(())
2708}
2709
2710fn show_state_basic<W: Write>(emu: &Emulator, stdout: &mut W, state: &State) -> io::Result<()> {
2714 if let Some(pos) = state.start_point {
2715 let mut label = None;
2716 for (lb, p) in emu.all_label_list.iter() {
2717 if *p == pos {
2718 label = Some(lb);
2719 break;
2720 }
2721 }
2722 if label.is_none() {
2723 for (lb, (p, _)) in emu.labels_for_debug.iter() {
2724 if *p == pos {
2725 label = Some(lb);
2726 break;
2727 }
2728 }
2729 }
2730 if label.is_none() {
2731 for (lb, p) in emu.alias_labels.iter() {
2732 if *p == pos {
2733 label = Some(lb);
2734 break;
2735 }
2736 }
2737 }
2738 if let Some(lb) = label {
2739 writeln!(stdout, "Start Point: {}", lb)?;
2740 } else {
2741 writeln!(stdout, "Start Point: #{:04X}", pos)?;
2742 }
2743 } else {
2744 writeln!(stdout, "Start Point: {}", emu.start_point.as_ref().unwrap())?;
2745 }
2746
2747 writeln!(stdout, "Step Count: {}", emu.basic_step_count)?;
2748
2749 write!(stdout, "Call State:")?;
2750 for (i, (pos, _)) in emu.program_stack.iter().enumerate() {
2751 if i > 0 {
2752 write!(stdout, " >")?;
2753 }
2754 if let Some((k, _)) = emu.all_label_list.iter().find(|(_, v)| v == pos) {
2755 write!(stdout, " {}", k)?;
2756 } else {
2757 write!(stdout, " ????")?;
2758 }
2759 }
2760 writeln!(stdout)?;
2761
2762 let bp = if emu.break_points[emu.last_run_position] {
2763 "*"
2764 } else {
2765 " "
2766 };
2767
2768 writeln!(stdout, "Last:")?;
2769 if let Some(((pg_label, info), hint_id)) = emu
2770 .get_current_program()
2771 .and_then(|(file, _, _)| emu.basic_info.get(file))
2772 .zip(emu.basic_step)
2773 {
2774 if let Some((n, s, extra)) = info.status_hint.get(hint_id as usize) {
2775 writeln!(stdout, "{0:5}: {1} {2:3$}{4}", hint_id, bp, "", n * 2, s)?;
2776 if let Some(extra) = extra {
2777 use compiler::ExtraInfo::*;
2778 writeln!(stdout, "Info:")?;
2779 match extra {
2780 For { counter, to, step } => {
2781 let local_labels = emu.local_labels.get(pg_label).unwrap();
2782 match emu.resolve_label(pg_label, counter) {
2783 Some((pos, None, parser::VarType::Integer)) => {
2784 write!(stdout, " counter: {}", emu.mem[pos] as i16)?;
2785 if let Some(to) = to {
2786 let to = emu.mem[*local_labels.get(to).unwrap()];
2787 write!(stdout, ", to: {}", to)?;
2788 }
2789 if let Some(step) = step {
2790 let step = emu.mem[*local_labels.get(step).unwrap()];
2791 write!(stdout, ", step: {}", step)?;
2792 }
2793 writeln!(stdout)?;
2794 }
2795 _ => writeln!(stdout, " counter: (データ破損)")?,
2796 }
2797 }
2798 Condition(reg) => {
2799 let v = match emu.general_registers[*reg as usize] {
2800 0x0000 => "False",
2801 0xFFFF => "True",
2802 _ => "????",
2803 };
2804 writeln!(stdout, " condition: {}", v)?;
2805 }
2806 RelatedCode(code) => writeln!(stdout, " related code: {}", code)?,
2807 SelectInt(reg) => {
2808 writeln!(stdout, " value: {}", emu.general_registers[*reg as usize])?;
2809 }
2810 SelectStr {
2811 len_value,
2812 pos_address,
2813 } => {
2814 let len = emu.general_registers[*len_value as usize] as usize;
2815 let pos = emu.general_registers[*pos_address as usize] as usize;
2816 if len <= 256 {
2817 let s = emu.mem[pos..pos + len]
2818 .iter()
2819 .map(|v| jis_x_201::convert_to_char(*v as u8, true))
2820 .collect::<String>()
2821 .replace('"', r#""""#);
2822 writeln!(stdout, r#" value: "{}""#, s)?;
2823 } else {
2824 writeln!(stdout, " value: (データ破損)")?;
2825 }
2826 }
2827 }
2828 }
2829 } else {
2830 writeln!(stdout, "{0:5}: {1} ?????", hint_id, bp)?;
2831 }
2832 } else {
2833 writeln!(stdout, "?????: {} ?????", bp)?;
2834 }
2835
2836 if let Some(err) = state.err.as_ref() {
2837 writeln!(stdout, "Program State:")?;
2838 writeln!(stdout, " {}", err)?;
2839 }
2840
2841 Ok(())
2842}
2843
2844fn show_state<W: Write>(emu: &Emulator, stdout: &mut W, state: &State) -> io::Result<()> {
2848 if let Some(pos) = state.start_point {
2849 let mut label = None;
2850 for (lb, p) in emu.all_label_list.iter() {
2851 if *p == pos {
2852 label = Some(lb);
2853 break;
2854 }
2855 }
2856 if label.is_none() {
2857 for (lb, (p, _)) in emu.labels_for_debug.iter() {
2858 if *p == pos {
2859 label = Some(lb);
2860 break;
2861 }
2862 }
2863 }
2864 if label.is_none() {
2865 for (lb, p) in emu.alias_labels.iter() {
2866 if *p == pos {
2867 label = Some(lb);
2868 break;
2869 }
2870 }
2871 }
2872 if let Some(lb) = label {
2873 writeln!(stdout, "Start Point: {}", lb)?;
2874 } else {
2875 writeln!(stdout, "Start Point: #{:04X}", pos)?;
2876 }
2877 } else {
2878 writeln!(stdout, "Start Point: {}", emu.start_point.as_ref().unwrap())?;
2879 }
2880
2881 writeln!(stdout, "Step Count: {}", emu.step_count)?;
2882
2883 write!(stdout, "Call State:")?;
2884 for (i, (pos, ret)) in emu.program_stack.iter().enumerate() {
2885 let fp = ret.checked_sub(2).unwrap();
2886 match emu.all_label_list.binary_search_by_key(pos, |(_, p)| *p) {
2887 Ok(mut index) => {
2888 while index > 0 {
2889 if emu
2890 .all_label_list
2891 .get(index - 1)
2892 .filter(|(_, p)| p == pos)
2893 .is_some()
2894 {
2895 index -= 1;
2896 continue;
2897 }
2898 break;
2899 }
2900 let (label, _) = emu.all_label_list.get(index).unwrap();
2901 if i == 0 {
2902 write!(stdout, " {}", label)?;
2903 } else {
2904 write!(stdout, " [#{:04X}]-> {}", fp, label)?;
2905 }
2906 }
2907 Err(_) => {
2908 if let Some(label) = emu
2909 .labels_for_debug
2910 .iter()
2911 .find_map(|(k, (p, _))| if p == pos { Some(k) } else { None })
2912 .or_else(|| {
2913 emu.alias_labels.iter().find_map(
2914 |(k, p)| {
2915 if p == pos {
2916 Some(k)
2917 } else {
2918 None
2919 }
2920 },
2921 )
2922 })
2923 {
2924 if i == 0 {
2925 write!(stdout, " {}", label)?;
2926 } else {
2927 write!(stdout, " [#{:04X}]-> {}", fp, label)?;
2928 }
2929 } else if i == 0 {
2930 write!(stdout, " #{:04X}", pos)?;
2931 } else {
2932 write!(stdout, " [#{:04X}]-> #{:04X}", fp, pos)?;
2933 }
2934 }
2935 }
2936 }
2937 writeln!(stdout)?;
2938
2939 if !emu.wrong_ret.is_empty() {
2940 writeln!(stdout, "Wrong RET: {}", emu.wrong_ret.len())?;
2941 }
2942
2943 if state.err.is_none() {
2944 let last_pos = emu.last_run_position;
2945 let last_op_code = emu.mem[last_pos];
2946 let next_pos = emu.program_register;
2947 if last_op_code == RET_OP_CODE && next_pos >= 2 {
2948 writeln!(stdout, "Prev Code:")?;
2949 let prev_pos = next_pos - 2;
2950 let info = emu.get_code_info(prev_pos as u16);
2951 if let Some((_, src)) = info.src_code.as_ref() {
2952 writeln!(stdout, "{}", src)?;
2953 }
2954 writeln!(stdout, "{}", info.mem_code)?;
2955 }
2956 }
2957
2958 writeln!(stdout, "Last Code:")?;
2959 let last_pos = emu.last_run_position;
2960 let info = emu.get_code_info(last_pos as u16);
2961 if let Some((_, src)) = info.src_code.as_ref() {
2962 writeln!(stdout, "{}", src)?;
2963 }
2964 writeln!(stdout, "{}", info.mem_code)?;
2965
2966 if let Some(err) = state.err.as_ref() {
2967 writeln!(stdout, "Program State:")?;
2968 writeln!(stdout, " {}", err)?;
2969 } else {
2970 writeln!(stdout, "Next Code:")?;
2971 let next_pos = emu.program_register;
2972 let info = emu.get_code_info(next_pos as u16);
2973 if let Some((_, src)) = info.src_code.as_ref() {
2974 writeln!(stdout, "{}", src)?;
2975 }
2976 writeln!(stdout, "{}", info.mem_code)?;
2977 }
2978 Ok(())
2979}
2980
2981fn set_by_file<W: Write>(
2985 emu: &mut Emulator,
2986 stdout: &mut W,
2987 state: &mut State,
2988 param: Option<&str>,
2989 can_basic_mode: bool,
2990) -> io::Result<()> {
2991 let file = match param {
2992 Some(file) => file,
2993 None => {
2994 writeln!(stdout, "引数が必要です")?;
2995 return Ok(());
2996 }
2997 };
2998
2999 let path = Path::new(file);
3000 if !path.exists() || !path.is_file() {
3001 eprintln!("ファイルが見つかりません ({})", file);
3002 return Ok(());
3003 }
3004 if path.metadata()?.len() > 1_000_000 {
3005 eprintln!("ファイルサイズが大きすぎます ({})", file);
3006 return Ok(());
3007 }
3008
3009 let text = fs::read_to_string(file)?;
3010
3011 for line in text.lines() {
3012 let mut cmd_and_param = line.trim().splitn(2, ' ').map(|s| s.trim());
3013 let cmd = cmd_and_param.next().unwrap();
3014 match state.debug_mode {
3015 DebugMode::Casl2 => match cmd {
3016 "add-dc" => add_dc(emu, stdout, cmd_and_param.next())?,
3017 "add-ds" => add_ds(emu, stdout, cmd_and_param.next())?,
3018 "copy-mem" => copy_mem(emu, stdout, cmd_and_param.next())?,
3019 "default-cmd" => {
3020 if let Some(defcmd) = cmd_and_param.next() {
3021 if "none".eq_ignore_ascii_case(defcmd) {
3022 state.default_cmd[DebugMode::Casl2 as usize] = None;
3023 } else {
3024 state.default_cmd[DebugMode::Casl2 as usize] = Some(defcmd.to_string());
3025 }
3026 }
3027 if let Some(defcmd) = state.default_cmd[DebugMode::Casl2 as usize].as_ref() {
3028 writeln!(stdout, "デフォルトのデバッガコマンド: {}", defcmd)?;
3029 } else {
3030 writeln!(stdout, "デフォルトのデバッガコマンド: none")?;
3031 }
3032 }
3033 "dump-code" => dump_code(emu, stdout, cmd_and_param.next())?,
3034 "dump-mem" => dump_mem(emu, stdout, cmd_and_param.next())?,
3035 "fill-mem" => fill_mem(emu, stdout, cmd_and_param.next())?,
3036 "find-code" => find_code(emu, stdout, cmd_and_param.next())?,
3037 "find-src" => find_src(emu, stdout, cmd_and_param.next())?,
3038 "find-value" => find_value(emu, stdout, cmd_and_param.next())?,
3039 "help" => show_command_help(cmd_and_param.next(), stdout)?,
3040 "list-files" => list_files(emu, stdout)?,
3041 "mode" => {
3042 if let Some(param) = cmd_and_param.next() {
3043 if "basic".eq_ignore_ascii_case(param) {
3044 state.debug_mode = DebugMode::Basic;
3045 if can_basic_mode {
3046 writeln!(stdout, "BASICデバッグモードに切り替えます")?;
3047 } else {
3048 writeln!(stdout, "BASICデバッグモードは使用できません")?;
3049 }
3050 } else if "casl2".eq_ignore_ascii_case(param) {
3051 writeln!(stdout, "現在CASL2デバッグモードです")?;
3052 } else {
3053 writeln!(stdout, "引数が不正です")?;
3054 }
3055 } else {
3056 writeln!(stdout, "現在CASL2デバッグモードです")?;
3057 }
3058 }
3059 "remove-breakpoint" => set_breakpoint(emu, stdout, cmd_and_param.next(), false)?,
3060 "set-breakpoint" => set_breakpoint(emu, stdout, cmd_and_param.next(), true)?,
3061 "set-label" => set_label(emu, stdout, cmd_and_param.next())?,
3062 "set-mem" => set_mem(emu, stdout, cmd_and_param.next())?,
3063 "set-reg" => {
3064 let msg = set_reg(emu, cmd_and_param.next());
3065 writeln!(stdout, "{}", msg)?;
3066 }
3067 "set-start" => {
3068 if let Some(s) = cmd_and_param.next() {
3069 let s = s.to_ascii_uppercase();
3070 let mut tokenizer = casl2::Tokenizer::new(s.as_str());
3071 match tokenizer.extended_label() {
3072 Ok(Some(lb)) => {
3073 if !tokenizer.rest().is_empty() {
3074 writeln!(stdout, "引数が不正です")?;
3075 } else {
3076 match lb.get_address(emu) {
3077 Ok(adr) => {
3078 state.start_point = Some(adr);
3079 writeln!(stdout, "スタートポイントを{}に設定しました(次のrestartから有効です)", lb)?;
3080 }
3081 Err(msg) => writeln!(stdout, "{}", msg)?,
3082 }
3083 }
3084 }
3085 Ok(_) => writeln!(stdout, "引数が不正です")?,
3086 Err(msg) => writeln!(stdout, "{}", msg)?,
3087 }
3088 } else {
3089 state.start_point = None;
3090 let name = emu.start_point.as_ref().unwrap();
3091 writeln!(
3092 stdout,
3093 "スタートポイントを{}に戻しました(次のrestartから有効です)",
3094 name
3095 )?;
3096 }
3097 }
3098 "show-labels" => show_labels(emu, stdout, cmd_and_param.next())?,
3099 "show-mem" => show_mem(emu, stdout, cmd_and_param.next())?,
3100 "show-mem-stat" => show_mem_stat(emu, stdout, cmd_and_param.next())?,
3101 "show-reg" => show_reg(emu, stdout)?,
3102 "show-src" => show_src(emu, stdout, cmd_and_param.next())?,
3103 "show-state" => show_state(emu, stdout, state)?,
3104 "show-var" => show_var(emu, stdout, cmd_and_param.next())?,
3105 "write-code" => write_code(emu, stdout, cmd_and_param.next())?,
3106 "" => {}
3107 _ => {
3108 writeln!(stdout, "コメント行としてスキップします: {}", line)?;
3109 }
3110 },
3111 DebugMode::Basic => match cmd {
3112 "default-cmd" => {
3113 if let Some(defcmd) = cmd_and_param.next() {
3114 if "none".eq_ignore_ascii_case(defcmd) {
3115 state.default_cmd[DebugMode::Basic as usize] = None;
3116 } else {
3117 state.default_cmd[DebugMode::Basic as usize] = Some(defcmd.to_string());
3118 }
3119 }
3120 if let Some(defcmd) = state.default_cmd[DebugMode::Basic as usize].as_ref() {
3121 writeln!(stdout, "デフォルトのデバッガコマンド: {}", defcmd)?;
3122 } else {
3123 writeln!(stdout, "デフォルトのデバッガコマンド: none")?;
3124 }
3125 }
3126 "fill-arr" => fill_arr(emu, stdout, cmd_and_param.next())?,
3127 "help" => show_command_help_for_basic(cmd_and_param.next(), stdout)?,
3128 "list-files" => list_files(emu, stdout)?,
3129 "mode" => {
3130 if let Some(param) = cmd_and_param.next() {
3131 if "casl2".eq_ignore_ascii_case(param) {
3132 state.debug_mode = DebugMode::Casl2;
3133 writeln!(stdout, "CASL2デバッグモードに切り替えます")?;
3134 } else if "basic".eq_ignore_ascii_case(param) {
3135 writeln!(stdout, "現在BASICデバッグモードです")?;
3136 } else {
3137 writeln!(stdout, "引数が不正です")?;
3138 }
3139 } else {
3140 writeln!(stdout, "現在BASICデバッグモードです")?;
3141 }
3142 }
3143 "remove-breakpoint" => {
3144 set_breakpoint_for_basic(emu, stdout, cmd_and_param.next(), false)?
3145 }
3146 "set-breakpoint" => {
3147 set_breakpoint_for_basic(emu, stdout, cmd_and_param.next(), true)?
3148 }
3149 "set-elem" => set_elem(emu, stdout, cmd_and_param.next())?,
3150 "set-len" => set_len(emu, stdout, cmd_and_param.next())?,
3151 "set-start" => {
3152 if let Some(s) = cmd_and_param.next() {
3153 let name = s.to_ascii_uppercase();
3154 if let Some(pos) = emu.program_labels.get(&name) {
3155 state.start_point = Some(*pos);
3156 writeln!(
3157 stdout,
3158 "スタートポイントを{}に設定しました(次のrestartから有効です)",
3159 name
3160 )?;
3161 } else {
3162 writeln!(stdout, "プログラムエントリ名{}が見つかりません", name)?;
3163 }
3164 } else {
3165 state.start_point = None;
3166 let name = emu.start_point.as_ref().unwrap();
3167 writeln!(
3168 stdout,
3169 "スタートポイントを{}に戻しました(次のrestartから有効です)",
3170 name
3171 )?;
3172 }
3173 }
3174 "set-var" => set_var(emu, stdout, cmd_and_param.next())?,
3175 "show-execute-stat" => show_execute_stat(emu, stdout, cmd_and_param.next())?,
3176 "show-src" => show_src_basic(emu, stdout, cmd_and_param.next())?,
3177 "show-state" => show_state_basic(emu, stdout, state)?,
3178 "show-var" => show_var_for_basic(emu, stdout, cmd_and_param.next())?,
3179 "" => {}
3180 _ => {
3181 writeln!(stdout, "コメント行としてスキップします: {}", line)?;
3182 }
3183 },
3184 }
3185 }
3186
3187 Ok(())
3188}
3189
3190fn write_code<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3194 let (pos, cmd) = match param {
3195 None => {
3196 writeln!(stdout, "引数が必要です")?;
3197 return Ok(());
3198 }
3199 Some(param) => {
3200 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3201 let adr = iter.next().unwrap();
3202 let adr = match emu.get_address_by_label_str(adr) {
3203 Ok(adr) => adr,
3204 Err(msg) => {
3205 writeln!(stdout, "{}", msg)?;
3206 return Ok(());
3207 }
3208 };
3209 if let Some(cmd) = iter.next() {
3210 match parse_casl2_command(emu, cmd, true) {
3211 Ok(cmd) => (adr, cmd),
3212 Err(msg) => {
3213 writeln!(stdout, "{}", msg)?;
3214 return Ok(());
3215 }
3216 }
3217 } else {
3218 writeln!(stdout, "引数が不正です")?;
3219 return Ok(());
3220 }
3221 }
3222 };
3223
3224 match cmd {
3225 Code::In(param) => {
3226 let mut tokenizer = casl2::Tokenizer::new(¶m);
3227 match Value::take_all_values(emu, &mut tokenizer) {
3228 Err(msg) => {
3229 writeln!(stdout, "{}", msg)?;
3230 return Ok(());
3231 }
3232 Ok(params) => {
3233 if let [Value::Int(buf), Value::Int(len)] = params.as_slice() {
3234 let src = casl2::parse(&format!(
3235 r#"
3236 PUSH 0,GR1
3237 PUSH 0,GR2
3238 PUSH 0,GR3
3239 PUSH 0,GR4
3240 PUSH 0,GR5
3241 PUSH 0,GR6
3242 PUSH 0,GR7
3243 LD GR1,GR0
3244 PUSH 0,GR1
3245 LAD GR1,{buf}
3246 LAD GR2,{len}
3247 SVC 1
3248 POP GR1
3249 LD GR0,GR1
3250 POP GR7
3251 POP GR6
3252 POP GR5
3253 POP GR4
3254 POP GR3
3255 POP GR2
3256 POP GR1"#,
3257 buf = buf,
3258 len = len
3259 ))
3260 .unwrap();
3261 let end = {
3262 let mut pos = pos;
3263 for stmt in src.iter() {
3264 if let casl2::Statement::Code { command, .. } = stmt {
3265 let len = command.len();
3266 emu.mem[pos] = command.first_word();
3267 match command {
3268 casl2::Command::P { adr, .. }
3269 | casl2::Command::A { adr, .. } => match adr {
3270 casl2::Adr::Dec(d) => emu.mem[pos + 1] = *d as u16,
3271 casl2::Adr::Hex(h) => emu.mem[pos + 1] = *h,
3272 _ => {}
3273 },
3274 _ => {}
3275 }
3276 pos += len;
3277 }
3278 }
3279 pos
3280 };
3281 writeln!(
3282 stdout,
3283 "#{:04X}..#{:04X}にIN #{:04X},#{:04X}を書き込みました",
3284 pos,
3285 end - 1,
3286 buf,
3287 len
3288 )?;
3289 } else {
3290 writeln!(stdout, "引数が不正です")?;
3291 return Ok(());
3292 }
3293 }
3294 }
3295 }
3296 Code::Out(param) => {
3297 let mut tokenizer = casl2::Tokenizer::new(¶m);
3298 match Value::take_all_values(emu, &mut tokenizer) {
3299 Err(msg) => {
3300 writeln!(stdout, "{}", msg)?;
3301 return Ok(());
3302 }
3303 Ok(params) => {
3304 if let [Value::Int(buf), Value::Int(len)] = params.as_slice() {
3305 let src = casl2::parse(&format!(
3306 r#"
3307 PUSH 0,GR1
3308 PUSH 0,GR2
3309 PUSH 0,GR3
3310 PUSH 0,GR4
3311 PUSH 0,GR5
3312 PUSH 0,GR6
3313 PUSH 0,GR7
3314 LD GR1,GR0
3315 PUSH 0,GR1
3316 LAD GR1,{buf}
3317 LAD GR2,{len}
3318 SVC 2
3319 POP GR1
3320 LD GR0,GR1
3321 POP GR7
3322 POP GR6
3323 POP GR5
3324 POP GR4
3325 POP GR3
3326 POP GR2
3327 POP GR1"#,
3328 buf = buf,
3329 len = len
3330 ))
3331 .unwrap();
3332 let end = {
3333 let mut pos = pos;
3334 for stmt in src.iter() {
3335 if let casl2::Statement::Code { command, .. } = stmt {
3336 let len = command.len();
3337 emu.mem[pos] = command.first_word();
3338 match command {
3339 casl2::Command::P { adr, .. }
3340 | casl2::Command::A { adr, .. } => match adr {
3341 casl2::Adr::Dec(d) => emu.mem[pos + 1] = *d as u16,
3342 casl2::Adr::Hex(h) => emu.mem[pos + 1] = *h,
3343 _ => {}
3344 },
3345 _ => {}
3346 }
3347 pos += len;
3348 }
3349 }
3350 pos
3351 };
3352 writeln!(
3353 stdout,
3354 "#{:04X}..#{:04X}にOUT #{:04X},#{:04X}を書き込みました",
3355 pos,
3356 end - 1,
3357 buf,
3358 len
3359 )?;
3360 } else {
3361 writeln!(stdout, "引数が不正です")?;
3362 return Ok(());
3363 }
3364 }
3365 }
3366 }
3367 Code::Casl2(cmd) => {
3368 let cmd_len = cmd.len();
3369 if pos
3370 .checked_add(cmd_len)
3371 .filter(|v| *v + 1 < emu.mem.len())
3372 .is_none()
3373 {
3374 writeln!(stdout, "メモリに十分な領域がありません")?;
3375 return Ok(());
3376 }
3377 match &cmd {
3378 casl2::Command::Start { .. } | casl2::Command::End | casl2::Command::Ds { .. } => {
3379 writeln!(stdout, "指定できないコマンドです")?;
3380 return Ok(());
3381 }
3382 casl2::Command::A { adr, .. } | casl2::Command::P { adr, .. } => {
3383 assert_eq!(cmd.len(), 2);
3384 let adr = adr.to_string();
3385 let mut tokenizer = casl2::Tokenizer::new(&adr);
3386 match Value::take_single_value(emu, &mut tokenizer) {
3387 Err(msg) => {
3388 writeln!(stdout, "{}", msg)?;
3389 return Ok(());
3390 }
3391 Ok(Value::Int(adr)) => {
3392 emu.mem[pos] = cmd.first_word();
3393 emu.mem[pos + 1] = adr as u16;
3394 }
3395 Ok(Value::Str(s)) => {
3396 if let Some(ch) = s.chars().next() {
3397 let ch = jis_x_201::convert_from_char(ch);
3398 emu.mem[pos] = cmd.first_word();
3399 emu.mem[pos + 1] = ch as u16;
3400 } else {
3401 writeln!(stdout, "引数が不正です")?;
3402 return Ok(());
3403 }
3404 }
3405 }
3406 }
3407 casl2::Command::R { .. }
3408 | casl2::Command::Pop { .. }
3409 | casl2::Command::Ret
3410 | casl2::Command::Nop => {
3411 assert_eq!(cmd.len(), 1);
3412 emu.mem[pos] = cmd.first_word();
3413 }
3414 casl2::Command::Dc { constants } => {
3415 let mut pos = pos;
3416 for c in constants.iter() {
3417 let c = c.to_string();
3418 let mut tokenizer = casl2::Tokenizer::new(&c);
3419 match Value::take_single_value(emu, &mut tokenizer) {
3420 Err(msg) => {
3421 writeln!(stdout, "{}", msg)?;
3422 return Ok(());
3423 }
3424 Ok(Value::Int(v)) => {
3425 emu.mem[pos] = v;
3426 pos += 1;
3427 }
3428 Ok(Value::Str(s)) => {
3429 for ch in s.chars() {
3430 let ch = jis_x_201::convert_from_char(ch);
3431 emu.mem[pos] = ch as u16;
3432 pos += 1;
3433 }
3434 }
3435 }
3436 }
3437 }
3438 casl2::Command::Rpush => {
3439 use casl2::IndexRegister::*;
3440 for (i, gr) in [Gr1, Gr2, Gr3, Gr4, Gr5, Gr6, Gr7].iter().enumerate() {
3441 let cmd = casl2::Command::P {
3442 code: casl2::P::Push,
3443 adr: casl2::Adr::Hex(0),
3444 x: Some(*gr),
3445 };
3446 emu.mem[pos + i] = cmd.first_word();
3447 }
3448 }
3449 casl2::Command::Rpop => {
3450 use casl2::Register::*;
3451 for (i, gr) in [Gr7, Gr6, Gr5, Gr4, Gr3, Gr2, Gr1].iter().enumerate() {
3452 let cmd = casl2::Command::Pop { r: *gr };
3453 emu.mem[pos + i] = cmd.first_word();
3454 }
3455 }
3456 casl2::Command::Out { .. }
3457 | casl2::Command::In { .. }
3458 | casl2::Command::DebugBasicStep { .. } => unreachable!("BUG"),
3459 }
3460 writeln!(
3461 stdout,
3462 "#{:04X}..#{:04X}に{}を書き込みました",
3463 pos,
3464 pos + cmd_len - 1,
3465 cmd
3466 )?;
3467 }
3468 }
3469
3470 Ok(())
3471}
3472
3473fn show_src<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3477 let (adr, len) = match param {
3478 None => (emu.program_register, 40),
3479 Some(param) => {
3480 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3481 let adr = iter.next().unwrap();
3482 let adr = match emu.get_address_by_label_str(adr) {
3483 Ok(adr) => adr,
3484 Err(msg) => {
3485 writeln!(stdout, "{}", msg)?;
3486 return Ok(());
3487 }
3488 };
3489 if let Some(len) = iter.next() {
3490 match parse_just_u16_value(len) {
3491 Some(len) if len > 0 => (adr, len as usize),
3492 _ => {
3493 writeln!(stdout, "引数が不正です")?;
3494 return Ok(());
3495 }
3496 }
3497 } else {
3498 (adr, 40)
3499 }
3500 }
3501 };
3502
3503 if adr
3504 .checked_add(len)
3505 .filter(|v| *v <= emu.mem.len())
3506 .is_none()
3507 {
3508 writeln!(stdout, "長さの指定が大きすぎます")?;
3509 return Ok(());
3510 }
3511
3512 let mut pos = adr;
3513 let mut view_some = false;
3514
3515 for (_file, label, statements) in emu.program_list.iter() {
3516 if statements.is_empty() {
3517 continue;
3518 }
3519 let (fp, _) = statements.first().unwrap();
3520 let (lp, _) = statements.last().unwrap();
3521 if pos < *fp || *lp < pos {
3522 continue;
3523 }
3524 view_some = true;
3525 writeln!(stdout, "Program: {}", label)?;
3526 let mut view = false;
3527 for (p, stmt) in statements.iter().skip_while(move |(p, _)| *p < pos) {
3528 if pos < *p {
3529 if *p >= adr + len {
3530 if !view {
3531 writeln!(stdout, "#{:04X}の位置にコードはありませんでした", pos)?;
3532 }
3533 return Ok(());
3534 }
3535 pos = *p;
3536 }
3537 let bp = if emu.break_points[pos] { "*" } else { " " };
3538 view = true;
3539 writeln!(stdout, "#{:04X}: {} {}", pos, bp, stmt)?;
3540 }
3541 }
3542
3543 if !view_some {
3544 writeln!(stdout, "表示可能なコードがありませんでした")?;
3545 }
3546
3547 Ok(())
3548}
3549
3550fn show_mem_stat<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3554 let (adr, len) = match param {
3555 None => {
3556 writeln!(stdout, "引数が必要です")?;
3557 return Ok(());
3558 }
3559 Some(param) => {
3560 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3561 let adr = iter.next().unwrap();
3562 let adr = match emu.get_address_by_label_str(adr) {
3563 Ok(adr) => adr,
3564 Err(msg) => {
3565 writeln!(stdout, "{}", msg)?;
3566 return Ok(());
3567 }
3568 };
3569 if let Some(len) = iter.next() {
3570 match parse_just_u16_value(len) {
3571 Some(len) if len > 0 => (adr, len as usize),
3572 _ => {
3573 writeln!(stdout, "引数が不正です")?;
3574 return Ok(());
3575 }
3576 }
3577 } else {
3578 (adr, 1)
3579 }
3580 }
3581 };
3582
3583 if adr
3584 .checked_add(len)
3585 .filter(|v| *v <= emu.mem.len())
3586 .is_none()
3587 {
3588 writeln!(stdout, "長さの指定が大きすぎます")?;
3589 return Ok(());
3590 }
3591
3592 writeln!(stdout, "書込回数 読込回数 実行回数")?;
3593 for pos in adr..adr + len {
3594 let exe = emu.execute_count[pos];
3595 let acc = emu.access_count[pos];
3596 let upd = emu.update_count[pos];
3597 let info = emu.get_code_info(pos as u16);
3598 writeln!(stdout, "{:8} {:8} {:8} {}", upd, acc, exe, info.mem_code)?;
3599 }
3600
3601 Ok(())
3602}
3603
3604fn show_mem<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3608 let (adr, len, view) = match param {
3609 None => {
3610 writeln!(stdout, "引数が必要です")?;
3611 return Ok(());
3612 }
3613 Some(param) => {
3614 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3615 let adr = iter.next().unwrap();
3616 let adr = match emu.get_address_by_label_str(adr) {
3617 Ok(adr) => adr,
3618 Err(msg) => {
3619 writeln!(stdout, "{}", msg)?;
3620 return Ok(());
3621 }
3622 };
3623 if let Some(rest) = iter.next() {
3624 let mut iter = rest.splitn(2, ' ').map(|s| s.trim());
3625 let token = iter.next().unwrap();
3626 if let Some(rest) = iter.next() {
3627 let len = match parse_just_u16_value(token) {
3629 Some(len) if len > 0 => len,
3630 _ => {
3631 writeln!(stdout, "引数が不正です")?;
3632 return Ok(());
3633 }
3634 };
3635 if let Ok(view) = ViewType::try_from(rest) {
3636 (adr, len, Some(view))
3637 } else {
3638 writeln!(stdout, "引数が不正です")?;
3639 return Ok(());
3640 }
3641 } else {
3642 match parse_just_u16_value(token) {
3644 Some(len) if len > 0 => (adr, len, None),
3645 _ => {
3646 if let Ok(view) = ViewType::try_from(token) {
3647 (adr, 1, Some(view))
3648 } else {
3649 writeln!(stdout, "引数が不正です")?;
3650 return Ok(());
3651 }
3652 }
3653 }
3654 }
3655 } else {
3656 (adr, 1, None)
3657 }
3658 }
3659 };
3660
3661 if adr
3662 .checked_add(len as usize)
3663 .filter(|v| *v <= emu.mem.len())
3664 .is_none()
3665 {
3666 writeln!(stdout, "長さの指定が大きすぎます")?;
3667 return Ok(());
3668 }
3669
3670 writeln!(stdout, "#{:04X}から{}語分のデータ", adr, len)?;
3671
3672 use std::fmt::Write;
3673
3674 match view {
3675 Some(ViewType::Int) => {
3676 let mut line = String::new();
3677 let mut count = 0;
3678 for i in 0..len as usize {
3679 if count == 0 {
3680 write!(&mut line, "#{:04X}: ", adr + i).unwrap();
3681 }
3682 if count == 4 {
3683 line.push(' ');
3684 }
3685 write!(&mut line, " {:6}", emu.mem[adr + i] as i16).unwrap();
3686 count += 1;
3687 if count == 8 {
3688 count = 0;
3689 writeln!(stdout, "{}", line)?;
3690 line.clear();
3691 }
3692 }
3693 if !line.is_empty() {
3694 writeln!(stdout, "{}", line)?;
3695 }
3696 }
3697 Some(ViewType::Bool) => {
3698 let mut line = String::new();
3699 let mut count = 0;
3700 for i in 0..len as usize {
3701 if count == 0 {
3702 write!(&mut line, "#{:04X}: ", adr + i).unwrap();
3703 }
3704 if count == 4 {
3705 line.push(' ');
3706 }
3707 let s = match emu.mem[adr + i] {
3708 0x0000 => "False",
3709 0xFFFF => "True",
3710 _ => "?????",
3711 };
3712 write!(&mut line, " {:<5}", s).unwrap();
3713 count += 1;
3714 if count == 8 {
3715 count = 0;
3716 writeln!(stdout, "{}", line)?;
3717 line.clear();
3718 }
3719 }
3720 if !line.is_empty() {
3721 writeln!(stdout, "{}", line)?;
3722 }
3723 }
3724 Some(ViewType::Str) => {
3725 let mut line = String::new();
3726 for i in 0..len as usize {
3727 let ch = emu.mem[adr + i] as u8;
3728 line.push(jis_x_201::convert_to_char(ch, true));
3729 }
3730 writeln!(stdout, r#""{}""#, line.replace('"', r#""""#))?;
3731 writeln!(stdout, "{}", line)?;
3732 }
3733 None => {
3734 let mut line = String::new();
3735 let mut count = 0;
3736 for i in 0..len as usize {
3737 if count == 0 {
3738 write!(&mut line, "#{:04X}: ", adr + i).unwrap();
3739 }
3740 if count == 4 {
3741 line.push(' ');
3742 }
3743 write!(&mut line, " #{:04X}", emu.mem[adr + i]).unwrap();
3744 count += 1;
3745 if count == 8 {
3746 count = 0;
3747 writeln!(stdout, "{}", line)?;
3748 line.clear();
3749 }
3750 }
3751 if !line.is_empty() {
3752 writeln!(stdout, "{}", line)?;
3753 }
3754 }
3755 }
3756
3757 Ok(())
3758}
3759
3760fn find_src<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3764 let (adr, cmd) = match param {
3765 None => {
3766 writeln!(stdout, "引数が必要です")?;
3767 return Ok(());
3768 }
3769 Some(param) => {
3770 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3771 let adr = iter.next().unwrap();
3772 if let Some(cmd) = iter.next() {
3773 (adr, cmd)
3774 } else {
3775 writeln!(stdout, "引数が不正です")?;
3776 return Ok(());
3777 }
3778 }
3779 };
3780
3781 let adr = match emu.get_address_by_label_str(adr) {
3782 Ok(adr) => adr,
3783 Err(msg) => {
3784 writeln!(stdout, "{}", msg)?;
3785 return Ok(());
3786 }
3787 };
3788
3789 let cmd = match parse_casl2_command(emu, cmd, false) {
3790 Ok(Code::Casl2(cmd)) => cmd,
3791 Ok(_) => unreachable!("BUG"),
3792 Err(msg) => {
3793 writeln!(stdout, "{}", msg)?;
3794 return Ok(());
3795 }
3796 };
3797
3798 for (file, label, statements) in emu.program_list.iter() {
3799 if statements.is_empty() {
3800 continue;
3801 }
3802 let (fp, _) = statements.first().unwrap();
3803 let (lp, _) = statements.last().unwrap();
3804 if adr < *fp || *lp < adr {
3805 continue;
3806 }
3807 for (p, stmt) in statements.iter() {
3808 if *p < adr {
3809 continue;
3810 }
3811 if let casl2::Statement::Code { command, .. } = stmt {
3812 if command != &cmd {
3813 continue;
3814 }
3815 let bp = if emu.break_points[*p] { "*" } else { " " };
3816 writeln!(stdout, "Program: {} ({})", label, file)?;
3817 let lbs = emu
3818 .alias_labels
3819 .iter()
3820 .filter_map(|(k, pos)| if pos == p { Some(k) } else { None })
3821 .cloned()
3822 .collect::<Vec<_>>()
3823 .join(" ");
3824 if !lbs.is_empty() {
3825 writeln!(stdout, "Labels: {}", lbs)?;
3826 }
3827 writeln!(stdout, "#{:04X}: {} {}", p, bp, stmt)?;
3828 return Ok(());
3829 }
3830 }
3831 }
3832
3833 writeln!(stdout, "見つかりませんでした")?;
3834
3835 Ok(())
3836}
3837
3838fn fill_mem<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3842 let (adr, len, value) = match param {
3843 None => {
3844 writeln!(stdout, "引数が必要です")?;
3845 return Ok(());
3846 }
3847 Some(param) => {
3848 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3849 let adr = iter.next().unwrap();
3850 let mut iter = match iter.next() {
3851 Some(rest) => rest.splitn(2, ' ').map(|s| s.trim()),
3852 None => {
3853 writeln!(stdout, "引数が不正です")?;
3854 return Ok(());
3855 }
3856 };
3857 let len = iter.next().unwrap();
3858 if let Some(value) = iter.next() {
3859 (adr, len, value)
3860 } else {
3861 writeln!(stdout, "引数が不正です")?;
3862 return Ok(());
3863 }
3864 }
3865 };
3866
3867 let adr = match emu.get_address_by_label_str(adr) {
3868 Ok(adr) => adr,
3869 Err(msg) => {
3870 writeln!(stdout, "{}", msg)?;
3871 return Ok(());
3872 }
3873 };
3874
3875 let len = match len.parse::<u16>() {
3876 Ok(len) => len as usize,
3877 Err(_) => {
3878 let mut tokenizer = casl2::Tokenizer::new(len);
3879 if let Some(h) = tokenizer.ignore_case_hex() {
3880 if tokenizer.rest().is_empty() {
3881 h as usize
3882 } else {
3883 writeln!(stdout, "引数が不正です")?;
3884 return Ok(());
3885 }
3886 } else {
3887 writeln!(stdout, "引数が不正です")?;
3888 return Ok(());
3889 }
3890 }
3891 };
3892
3893 if adr
3894 .checked_add(len)
3895 .filter(|v| *v <= emu.mem.len())
3896 .is_none()
3897 {
3898 writeln!(stdout, "長さが大きすぎます")?;
3899 return Ok(());
3900 }
3901
3902 let mut tokenizer = casl2::Tokenizer::new(value);
3903 let value = match Value::take_all_values(emu, &mut tokenizer) {
3904 Err(msg) => {
3905 writeln!(stdout, "{}", msg)?;
3906 return Ok(());
3907 }
3908 Ok(values) => match values.as_slice() {
3909 [Value::Int(v)] => *v,
3910 [Value::Str(s)] if !s.is_empty() => {
3911 let ch = s.chars().next().unwrap();
3912 jis_x_201::convert_from_char(ch) as u16
3913 }
3914 _ => {
3915 writeln!(stdout, "引数が不正です")?;
3916 return Ok(());
3917 }
3918 },
3919 };
3920
3921 emu.mem[adr..adr + len].fill(value);
3922
3923 writeln!(
3924 stdout,
3925 "#{:04X}から{}語分を#{:04X}で埋めました",
3926 adr, len, value
3927 )?;
3928
3929 Ok(())
3930}
3931
3932fn set_label<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3936 let (label, adr) = match param {
3937 None => {
3938 writeln!(stdout, "引数が必要です")?;
3939 return Ok(());
3940 }
3941 Some(param) => {
3942 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3943 let label = iter.next().unwrap().to_ascii_uppercase();
3944 if !casl2::Label::from(&label).is_valid() {
3945 writeln!(stdout, "{}はラベルとして使用できません", label)?;
3946 return Ok(());
3947 }
3948 if let Some(adr) = iter.next() {
3949 (label, adr)
3950 } else {
3951 writeln!(stdout, "引数が不正です")?;
3952 return Ok(());
3953 }
3954 }
3955 };
3956
3957 if emu.program_labels.contains_key(&label) {
3958 writeln!(stdout, "{}は既に使用されており指定できません", label)?;
3959 return Ok(());
3960 }
3961 if emu.labels_for_debug.contains_key(&label) {
3962 writeln!(stdout, "{}は既に使用されており指定できません", label)?;
3963 return Ok(());
3964 }
3965
3966 let adr = match emu.get_address_by_label_str(adr) {
3967 Ok(adr) => adr,
3968 Err(msg) => {
3969 writeln!(stdout, "{}", msg)?;
3970 return Ok(());
3971 }
3972 };
3973
3974 if let Some(x) = emu.alias_labels.get(&label) {
3975 writeln!(stdout, "#{:04X}から#{:04X}に上書きされます", x, adr)?;
3976 }
3977 emu.alias_labels.insert(label.clone(), adr);
3978 writeln!(stdout, "ラベル{}に#{:04X}を設定しました", label, adr)?;
3979
3980 Ok(())
3981}
3982
3983fn find_value<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
3987 let (adr, value) = match param {
3988 None => {
3989 writeln!(stdout, "引数が必要です")?;
3990 return Ok(());
3991 }
3992 Some(param) => {
3993 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
3994 let adr = iter.next().unwrap();
3995 if let Some(value) = iter.next() {
3996 (adr, value)
3997 } else {
3998 writeln!(stdout, "引数が不正です")?;
3999 return Ok(());
4000 }
4001 }
4002 };
4003
4004 let adr = match emu.get_address_by_label_str(adr) {
4005 Err(msg) => {
4006 writeln!(stdout, "{}", msg)?;
4007 return Ok(());
4008 }
4009 Ok(adr) => adr,
4010 };
4011
4012 let mut tokenizer = casl2::Tokenizer::new(value);
4013 let value = match Value::take_all_values_without_literal(emu, &mut tokenizer) {
4014 Err(msg) => {
4015 writeln!(stdout, "{}", msg)?;
4016 return Ok(());
4017 }
4018 Ok(values) => match values.as_slice() {
4019 [Value::Int(v)] => *v,
4020 [Value::Str(s)] if !s.is_empty() => {
4021 let ch = s.chars().next().unwrap();
4022 jis_x_201::convert_from_char(ch) as u16
4023 }
4024 _ => {
4025 writeln!(stdout, "引数が不正です")?;
4026 return Ok(());
4027 }
4028 },
4029 };
4030
4031 if let Some((i, _)) = emu
4032 .mem
4033 .iter()
4034 .enumerate()
4035 .skip(adr)
4036 .find(|(_, mem)| **mem == value)
4037 {
4038 let info = emu.get_code_info(i as u16);
4039 if let Some(lb) = info.src_entry_label.as_ref() {
4040 if let Some(file) = info.src_file.as_ref() {
4041 writeln!(stdout, "Program: {} ({})", lb, file)?;
4042 } else {
4043 writeln!(stdout, "Program: {}", lb)?;
4044 }
4045 }
4046 if !info.alias_labels.is_empty() {
4047 writeln!(stdout, "Labels: {}", info.alias_labels.join(" "))?;
4048 }
4049 if let Some((_, src)) = info.src_code.as_ref() {
4050 writeln!(stdout, "{}", src)?;
4051 }
4052 writeln!(stdout, "{}", info.mem_code)?;
4053 return Ok(());
4054 }
4055
4056 writeln!(stdout, "見つかりませんでした")?;
4057 Ok(())
4058}
4059
4060fn find_code<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4064 let (adr, cmd) = match param {
4065 None => {
4066 writeln!(stdout, "引数が必要です")?;
4067 return Ok(());
4068 }
4069 Some(param) => {
4070 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
4071 let adr = iter.next().unwrap();
4072 if let Some(cmd) = iter.next() {
4073 (adr, cmd)
4074 } else {
4075 writeln!(stdout, "引数が不正です")?;
4076 return Ok(());
4077 }
4078 }
4079 };
4080
4081 let adr = match emu.get_address_by_label_str(adr) {
4082 Err(msg) => {
4083 writeln!(stdout, "{}", msg)?;
4084 return Ok(());
4085 }
4086 Ok(adr) => adr,
4087 };
4088
4089 let cmd = match parse_casl2_command(emu, cmd, true) {
4090 Err(msg) => {
4091 writeln!(stdout, "{}", msg)?;
4092 return Ok(());
4093 }
4094 Ok(Code::Casl2(cmd)) => cmd,
4095 Ok(_) => {
4096 writeln!(stdout, "指定できないコマンドです")?;
4097 return Ok(());
4098 }
4099 };
4100
4101 match &cmd {
4102 casl2::Command::Start { .. }
4103 | casl2::Command::End
4104 | casl2::Command::Ds { .. }
4105 | casl2::Command::Dc { .. }
4106 | casl2::Command::In { .. }
4107 | casl2::Command::Out { .. }
4108 | casl2::Command::Rpush
4109 | casl2::Command::Rpop
4110 | casl2::Command::DebugBasicStep { .. } => {
4111 writeln!(stdout, "指定できないコマンドです")?;
4112 return Ok(());
4113 }
4114 casl2::Command::R { .. }
4115 | casl2::Command::A { .. }
4116 | casl2::Command::P { .. }
4117 | casl2::Command::Pop { .. }
4118 | casl2::Command::Ret
4119 | casl2::Command::Nop => {}
4120 }
4121
4122 assert!((1..=2).contains(&cmd.len()));
4123
4124 let first_word = cmd.first_word();
4125
4126 let param_adr = match &cmd {
4127 casl2::Command::A { adr, .. } | casl2::Command::P { adr, .. } => match adr {
4128 casl2::Adr::Dec(d) => Some(*d as u16),
4129 casl2::Adr::Hex(h) => Some(*h),
4130 casl2::Adr::Label(lb) => match emu.get_address_by_label_str(lb.as_str()) {
4131 Ok(adr) => Some(adr as u16),
4132 Err(msg) => {
4133 writeln!(stdout, "{}", msg)?;
4134 return Ok(());
4135 }
4136 },
4137 casl2::Adr::LiteralDec(..)
4138 | casl2::Adr::LiteralHex(..)
4139 | casl2::Adr::LiteralStr(..) => {
4140 writeln!(stdout, "リテラルを指定することはできません")?;
4141 return Ok(());
4142 }
4143 },
4144 _ => None,
4145 };
4146
4147 for (i, mem) in emu.mem.iter().enumerate().skip(adr) {
4148 if *mem != first_word {
4149 continue;
4150 }
4151 if let Some(adr) = param_adr {
4152 if let Some(mem) = emu.mem.get(i + 1) {
4153 if *mem == adr {
4154 let info = emu.get_code_info(i as u16);
4155 if let Some(lb) = info.src_entry_label.as_ref() {
4156 if let Some(file) = info.src_file.as_ref() {
4157 writeln!(stdout, "Program: {} ({})", lb, file)?;
4158 } else {
4159 writeln!(stdout, "Program: {}", lb)?;
4160 }
4161 }
4162 if !info.alias_labels.is_empty() {
4163 writeln!(stdout, "Labels: {}", info.alias_labels.join(" "))?;
4164 }
4165 if let Some((_, src)) = info.src_code.as_ref() {
4166 writeln!(stdout, "{}", src)?;
4167 }
4168 writeln!(stdout, "{}", info.mem_code)?;
4169 return Ok(());
4170 }
4171 }
4172 } else {
4173 let info = emu.get_code_info(i as u16);
4174 if let Some(lb) = info.src_entry_label.as_ref() {
4175 if let Some(file) = info.src_file.as_ref() {
4176 writeln!(stdout, "Program: {} ({})", lb, file)?;
4177 } else {
4178 writeln!(stdout, "Program: {}", lb)?;
4179 }
4180 }
4181 if !info.alias_labels.is_empty() {
4182 writeln!(stdout, "Labels: {}", info.alias_labels.join(" "))?;
4183 }
4184 if let Some((_, src)) = info.src_code.as_ref() {
4185 writeln!(stdout, "{}", src)?;
4186 }
4187 writeln!(stdout, "{}", info.mem_code)?;
4188 return Ok(());
4189 }
4190 }
4191
4192 writeln!(stdout, "見つかりませんでした")?;
4193 Ok(())
4194}
4195
4196fn copy_mem<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4200 let params = if let Some(param) = param {
4201 param.split_whitespace().collect::<Vec<_>>()
4202 } else {
4203 writeln!(stdout, "引数が必要です")?;
4204 return Ok(());
4205 };
4206 let (adr1s, adr2s, len) = if let [adr1, adr2, len] = params.as_slice() {
4207 (adr1, adr2, len)
4208 } else {
4209 writeln!(stdout, "引数が不正です")?;
4210 return Ok(());
4211 };
4212
4213 let adr1 = match emu.get_address_by_label_str(adr1s) {
4214 Ok(adr) => adr,
4215 Err(msg) => {
4216 writeln!(stdout, "{}", msg)?;
4217 return Ok(());
4218 }
4219 };
4220
4221 let adr2 = match emu.get_address_by_label_str(adr2s) {
4222 Ok(adr) => adr,
4223 Err(msg) => {
4224 writeln!(stdout, "{}", msg)?;
4225 return Ok(());
4226 }
4227 };
4228
4229 let len = match len.parse::<u16>() {
4230 Ok(len) => len as usize,
4231 Err(_) => match casl2::Tokenizer::new(len).ignore_case_hex() {
4232 Some(len) => len as usize,
4233 None => {
4234 writeln!(stdout, "引数が不正です")?;
4235 return Ok(());
4236 }
4237 },
4238 };
4239
4240 if adr1
4241 .checked_add(len)
4242 .filter(|p| *p <= emu.mem.len())
4243 .is_none()
4244 {
4245 writeln!(stdout, "引数が不正です")?;
4246 return Ok(());
4247 }
4248
4249 if adr2
4250 .checked_add(len)
4251 .filter(|p| *p <= emu.mem.len())
4252 .is_none()
4253 {
4254 writeln!(stdout, "引数が不正です")?;
4255 return Ok(());
4256 }
4257
4258 emu.mem.copy_within(adr1..adr1 + len, adr2);
4259
4260 writeln!(
4261 stdout,
4262 "#{:04X}から#{:04X}へ{}語コピーしました",
4263 adr1, adr2, len
4264 )?;
4265
4266 Ok(())
4267}
4268
4269fn add_ds<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4273 let (name, size) = match param {
4274 None => {
4275 writeln!(stdout, "引数が必要です")?;
4276 return Ok(());
4277 }
4278 Some(param) => {
4279 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
4280 let name = iter.next().unwrap().to_ascii_uppercase();
4281 if !casl2::Label::from(&name).is_valid() {
4282 writeln!(stdout, "{}はラベルとして不正です", name)?;
4283 return Ok(());
4284 }
4285 match iter.next() {
4286 None => {
4287 writeln!(stdout, "引数が不足しています")?;
4288 return Ok(());
4289 }
4290 Some(size) => (name, size),
4291 }
4292 }
4293 };
4294
4295 if emu.program_labels.contains_key(&name) {
4296 writeln!(stdout, "{}は既に使用されており指定できません", name)?;
4297 return Ok(());
4298 }
4299 if emu.labels_for_debug.contains_key(&name) {
4300 writeln!(stdout, "{}は既に使用されており指定できません", name)?;
4301 return Ok(());
4302 }
4303 if emu.alias_labels.contains_key(&name) {
4304 writeln!(stdout, "{}は既に使用されており指定できません", name)?;
4305 return Ok(());
4306 }
4307
4308 let size = match size.parse::<u16>() {
4309 Ok(size) => size,
4310 Err(_) => {
4311 let mut tokenizer = casl2::Tokenizer::new(size);
4312 if let Some(h) = tokenizer.ignore_case_hex() {
4313 h
4314 } else {
4315 writeln!(stdout, "サイズ指定が不正です")?;
4316 return Ok(());
4317 }
4318 }
4319 };
4320
4321 if !emu.enough_remain(size as usize) {
4322 writeln!(stdout, "メモリに十分な領域がありません")?;
4323 return Ok(());
4324 }
4325
4326 let adr = emu.compile_pos;
4327 emu.compile_pos += size as usize;
4328
4329 let stmt = casl2::Statement::labeled(&name, casl2::Command::Ds { size });
4330
4331 emu.labels_for_debug.insert(name.clone(), (adr, stmt));
4332
4333 writeln!(
4334 stdout,
4335 "{}(#{:04X})に{}語分の領域を確保しました",
4336 name, adr, size
4337 )?;
4338
4339 Ok(())
4340}
4341
4342fn add_dc<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4346 let (name, values) = match param {
4347 None => {
4348 writeln!(stdout, "引数が必要です")?;
4349 return Ok(());
4350 }
4351 Some(param) => {
4352 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
4353 let name = iter.next().unwrap().to_ascii_uppercase();
4354 if !casl2::Label::from(&name).is_valid() {
4355 writeln!(stdout, "{}はラベルとして不正です", name)?;
4356 return Ok(());
4357 }
4358 match iter.next() {
4359 None => {
4360 writeln!(stdout, "引数が不足しています")?;
4361 return Ok(());
4362 }
4363 Some(values) => (name, values),
4364 }
4365 }
4366 };
4367
4368 if emu.program_labels.contains_key(&name) {
4369 writeln!(stdout, "{}は既に使用されており指定できません", name)?;
4370 return Ok(());
4371 }
4372 if emu.labels_for_debug.contains_key(&name) {
4373 writeln!(stdout, "{}は既に使用されており指定できません", name)?;
4374 return Ok(());
4375 }
4376 if emu.alias_labels.contains_key(&name) {
4377 writeln!(stdout, "{}は既に使用されており指定できません", name)?;
4378 return Ok(());
4379 }
4380
4381 let mut tokenizer = casl2::Tokenizer::new(values);
4382 let values = match Value::take_all_values(emu, &mut tokenizer) {
4383 Ok(values) => values,
4384 Err(msg) => {
4385 writeln!(stdout, "{}", msg)?;
4386 return Ok(());
4387 }
4388 };
4389
4390 let len = values
4391 .iter()
4392 .map(|v| match v {
4393 Value::Int(_) => 1,
4394 Value::Str(s) => s.chars().count(),
4395 })
4396 .sum::<usize>();
4397
4398 if !emu.enough_remain(len) {
4399 writeln!(stdout, "メモリに十分な領域がありません")?;
4400 return Ok(());
4401 }
4402
4403 let adr = emu.compile_pos;
4404 emu.compile_pos += len;
4405
4406 let mut constants = vec![];
4407 let mut pos = adr;
4408 let mut msg = format!("{}(#{:04X})に", name, pos);
4409 for v in values {
4410 match v {
4411 Value::Int(v) => {
4412 emu.mem[pos] = v;
4413 pos += 1;
4414 msg.push_str(&format!("#{:04X},", v));
4415 constants.push(casl2::Constant::Hex(v));
4416 }
4417 Value::Str(s) => {
4418 for ch in s.chars() {
4419 let v = jis_x_201::convert_from_char(ch) as u16;
4420 emu.mem[pos] = v;
4421 pos += 1;
4422 msg.push_str(&format!("#{:04X},", v));
4423 constants.push(casl2::Constant::Hex(v));
4424 }
4425 }
4426 }
4427 }
4428
4429 let stmt = casl2::Statement::labeled(&name, casl2::Command::Dc { constants });
4430
4431 emu.labels_for_debug.insert(name, (adr, stmt));
4432
4433 writeln!(stdout, "{}を設定しました", msg)?;
4434
4435 Ok(())
4436}
4437
4438fn set_mem<W: Write>(emu: &mut Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4442 let (adr, values) = match param {
4443 None => {
4444 writeln!(stdout, "引数が必要です")?;
4445 return Ok(());
4446 }
4447 Some(param) => {
4448 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
4449 let adr = iter.next().unwrap();
4450 match iter.next() {
4451 None => {
4452 writeln!(stdout, "引数が不足しています")?;
4453 return Ok(());
4454 }
4455 Some(values) => (adr, values),
4456 }
4457 }
4458 };
4459
4460 let adr = adr.to_ascii_uppercase();
4461 let mut tokenizer = casl2::Tokenizer::new(adr.as_str());
4462 let adr = match tokenizer.extended_label() {
4463 Err(msg) => {
4464 writeln!(stdout, "{}", msg)?;
4465 return Ok(());
4466 }
4467 Ok(None) => {
4468 writeln!(stdout, "引数が不正です")?;
4469 return Ok(());
4470 }
4471 Ok(Some(lb)) => {
4472 if !tokenizer.rest().is_empty() {
4473 writeln!(stdout, "引数が不正です")?;
4474 return Ok(());
4475 }
4476 match lb.get_address(emu) {
4477 Ok(adr) => adr,
4478 Err(msg) => {
4479 writeln!(stdout, "{}", msg)?;
4480 return Ok(());
4481 }
4482 }
4483 }
4484 };
4485
4486 let mut tokenizer = casl2::Tokenizer::new(values);
4487 let values = match Value::take_all_values(emu, &mut tokenizer) {
4488 Ok(values) => values,
4489 Err(msg) => {
4490 writeln!(stdout, "{}", msg)?;
4491 return Ok(());
4492 }
4493 };
4494
4495 let len = values
4496 .iter()
4497 .map(|v| match v {
4498 Value::Int(_) => 1,
4499 Value::Str(s) => s.chars().count(),
4500 })
4501 .sum::<usize>();
4502
4503 if adr + len > emu.mem.len() {
4504 writeln!(stdout, "十分な領域がありません")?;
4505 return Ok(());
4506 }
4507
4508 let mut pos = adr;
4509 let mut msg = format!("#{:04X}に", pos);
4510 for v in values {
4511 match v {
4512 Value::Int(v) => {
4513 emu.mem[pos] = v;
4514 pos += 1;
4515 msg.push_str(&format!("#{:04X},", v));
4516 }
4517 Value::Str(s) => {
4518 for ch in s.chars() {
4519 let v = jis_x_201::convert_from_char(ch) as u16;
4520 emu.mem[pos] = v;
4521 pos += 1;
4522 msg.push_str(&format!("#{:04X},", v));
4523 }
4524 }
4525 }
4526 }
4527
4528 writeln!(stdout, "{}を設定しました", msg)?;
4529
4530 Ok(())
4531}
4532
4533fn set_breakpoint<W: Write>(
4537 emu: &mut Emulator,
4538 stdout: &mut W,
4539 param: Option<&str>,
4540 value: bool,
4541) -> io::Result<()> {
4542 let adr_list = match param.map(|s| s.to_ascii_uppercase()) {
4543 None => vec![emu.program_register],
4544 Some(param) => {
4545 let mut vs = vec![];
4546 let mut tokenizer = casl2::Tokenizer::new(¶m);
4547 loop {
4548 if let Some(adr) = tokenizer.ignore_case_hex() {
4549 vs.push(adr as usize);
4550 } else {
4551 let label = match tokenizer.extended_label() {
4552 Ok(Some(label)) => label,
4553 Ok(None) => {
4554 writeln!(stdout, "引数が不正です")?;
4555 return Ok(());
4556 }
4557 Err(msg) => {
4558 writeln!(stdout, "{}", msg)?;
4559 return Ok(());
4560 }
4561 };
4562 match label.get_address(emu) {
4563 Ok(adr) => vs.push(adr),
4564 Err(msg) => {
4565 writeln!(stdout, "{}", msg)?;
4566 return Ok(());
4567 }
4568 }
4569 }
4570 if !tokenizer.comma() {
4571 if tokenizer.rest().is_empty() {
4572 break;
4573 } else {
4574 writeln!(stdout, "引数が不正です")?;
4575 return Ok(());
4576 }
4577 }
4578 }
4579 vs
4580 }
4581 };
4582
4583 for adr in adr_list {
4584 if value {
4585 emu.break_points[adr] = true;
4586 writeln!(stdout, "#{:04X}にブレークポイントを設定しました", adr)?;
4587 } else if emu.break_points[adr] {
4588 emu.break_points[adr] = false;
4589 writeln!(stdout, "#{:04X}のブレークポイントを解除しました", adr)?;
4590 } else {
4591 writeln!(stdout, "#{:04X}にブレークポイントは設定されていません", adr)?;
4592 }
4593
4594 let info = emu.get_code_info(adr as u16);
4595 if let Some((_, src)) = info.src_code.as_ref() {
4596 writeln!(stdout, "{}", src)?;
4597 }
4598 writeln!(stdout, "{}", info.mem_code)?;
4599 }
4600
4601 Ok(())
4602}
4603
4604fn dump_mem<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4608 let (adr, size) = match param.map(|s| s.to_ascii_uppercase()) {
4609 None => {
4610 let adr = emu.program_register;
4611 let size = (adr + 0x80).min(0x10000) - adr;
4612 (adr, size)
4613 }
4614 Some(param) => {
4615 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
4616 let adr = iter.next().unwrap();
4617 let size = iter.next();
4618 let mut tokenizer = casl2::Tokenizer::new(adr);
4619 let adr = if let Some(adr) = tokenizer.ignore_case_hex() {
4620 if !tokenizer.rest().trim().is_empty() {
4621 writeln!(stdout, "引数が不正です")?;
4622 return Ok(());
4623 }
4624 adr as usize
4625 } else {
4626 match tokenizer.extended_label() {
4627 Ok(Some(label)) => {
4628 if !tokenizer.rest().trim().is_empty() {
4629 writeln!(stdout, "引数が不正です")?;
4630 return Ok(());
4631 }
4632 match label.get_address(emu) {
4633 Ok(adr) => adr,
4634 Err(msg) => {
4635 writeln!(stdout, "{}", msg)?;
4636 return Ok(());
4637 }
4638 }
4639 }
4640 Ok(_) => {
4641 writeln!(stdout, "引数が不正です")?;
4642 return Ok(());
4643 }
4644 Err(msg) => {
4645 writeln!(stdout, "{}", msg)?;
4646 return Ok(());
4647 }
4648 }
4649 };
4650 let size = match size {
4651 None => 0x80,
4652 Some(s) => {
4653 let mut tokenizer = casl2::Tokenizer::new(s);
4654 if let Some(v) = tokenizer.ignore_case_hex() {
4655 if !tokenizer.rest().trim().is_empty() {
4656 writeln!(stdout, "引数が不正です")?;
4657 return Ok(());
4658 }
4659 v as usize
4660 } else if let Some(v) = tokenizer.integer() {
4661 if !tokenizer.rest().trim().is_empty() {
4662 writeln!(stdout, "引数が不正です")?;
4663 return Ok(());
4664 }
4665 if v > 0 {
4666 v as usize
4667 } else {
4668 writeln!(stdout, "引数が不正です")?;
4669 return Ok(());
4670 }
4671 } else {
4672 writeln!(stdout, "引数が不正です")?;
4673 return Ok(());
4674 }
4675 }
4676 };
4677 if adr + size <= 0x10000 {
4678 (adr, size)
4679 } else {
4680 (adr, 0x10000 - adr)
4681 }
4682 }
4683 };
4684 let mut pos = adr & (0xFFFF ^ 0x7);
4685 write!(stdout, " ")?;
4686 for i in 0..8 {
4687 if i == 4 {
4688 write!(stdout, " ")?;
4689 }
4690 write!(stdout, " {:4X}", (pos & 0x8) + i)?;
4691 }
4692 writeln!(stdout)?;
4693 write!(stdout, " ")?;
4694 for i in 0..8 {
4695 if i == 4 {
4696 write!(stdout, " ")?;
4697 }
4698 write!(stdout, " {:4X}", ((pos & 0x8) + i) ^ 0x8)?;
4699 }
4700 writeln!(stdout)?;
4701 let mut s = String::new();
4702 while pos < adr + size && pos < 0x10000 {
4703 s.clear();
4704 write!(stdout, "#{:04X}: ", pos)?;
4705 for i in 0..8 {
4706 if i == 4 {
4707 write!(stdout, " ")?;
4708 s.push(' ');
4709 }
4710 if (adr..adr + size).contains(&(pos + i)) {
4711 let v = emu.mem[pos + i];
4712 write!(stdout, " {:04X}", v)?;
4713 s.push(jis_x_201::convert_to_char((v >> 8) as u8, true));
4714 s.push(jis_x_201::convert_to_char(v as u8, true));
4715 } else {
4716 write!(stdout, " ....")?;
4717 s.push_str("..");
4718 }
4719 }
4720 writeln!(stdout, " {}", s)?;
4721 pos += 8;
4722 }
4723 Ok(())
4724}
4725
4726fn dump_code<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4730 let (adr, size) = match param.map(|s| s.to_ascii_uppercase()) {
4731 None => {
4732 let adr = emu.program_register;
4733 let size = (adr + 0x20).min(0x10000) - adr;
4734 (adr, size)
4735 }
4736 Some(param) => {
4737 let mut iter = param.splitn(2, ' ').map(|s| s.trim());
4738 let adr = iter.next().unwrap();
4739 let size = iter.next();
4740 let mut tokenizer = casl2::Tokenizer::new(adr);
4741 let adr = if let Some(adr) = tokenizer.ignore_case_hex() {
4742 if !tokenizer.rest().trim().is_empty() {
4743 writeln!(stdout, "引数が不正です")?;
4744 return Ok(());
4745 }
4746 adr as usize
4747 } else {
4748 match tokenizer.extended_label() {
4749 Ok(Some(label)) => {
4750 if !tokenizer.rest().trim().is_empty() {
4751 writeln!(stdout, "引数が不正です")?;
4752 return Ok(());
4753 }
4754 match label.get_address(emu) {
4755 Ok(adr) => adr,
4756 Err(msg) => {
4757 writeln!(stdout, "{}", msg)?;
4758 return Ok(());
4759 }
4760 }
4761 }
4762 Ok(_) => {
4763 writeln!(stdout, "引数が不正です")?;
4764 return Ok(());
4765 }
4766 Err(msg) => {
4767 writeln!(stdout, "{}", msg)?;
4768 return Ok(());
4769 }
4770 }
4771 };
4772 let size = match size {
4773 None => 0x20,
4774 Some(s) => {
4775 let mut tokenizer = casl2::Tokenizer::new(s);
4776 if let Some(v) = tokenizer.ignore_case_hex() {
4777 if !tokenizer.rest().trim().is_empty() {
4778 writeln!(stdout, "引数が不正です")?;
4779 return Ok(());
4780 }
4781 v as usize
4782 } else if let Some(v) = tokenizer.integer() {
4783 if !tokenizer.rest().trim().is_empty() {
4784 writeln!(stdout, "引数が不正です")?;
4785 return Ok(());
4786 }
4787 if v > 0 {
4788 v as usize
4789 } else {
4790 writeln!(stdout, "引数が不正です")?;
4791 return Ok(());
4792 }
4793 } else {
4794 writeln!(stdout, "引数が不正です")?;
4795 return Ok(());
4796 }
4797 }
4798 };
4799 if adr + size <= 0x10000 {
4800 (adr, size)
4801 } else {
4802 (adr, 0x10000 - adr)
4803 }
4804 }
4805 };
4806 let mut pos = adr;
4807 while pos < adr + size && pos < 0x10000 {
4808 let info = emu.get_code_info(pos as u16);
4809 writeln!(stdout, "{}", info.mem_code)?;
4810 pos += get_op_code_size(emu.mem[pos]).max(1);
4811 }
4812 Ok(())
4813}
4814
4815fn list_files<W: Write>(emu: &Emulator, stdout: &mut W) -> io::Result<()> {
4819 let mut last: Option<&str> = None;
4820 for (file, key, _) in emu.program_list.iter() {
4821 if let Some(f) = last.as_ref() {
4822 if *f == file.as_str() {
4823 write!(stdout, " {}", key)?;
4824 continue;
4825 }
4826 }
4827 writeln!(stdout)?;
4828 writeln!(stdout, " {}", file)?;
4829 write!(stdout, " {}", key)?;
4830 last = Some(file.as_str());
4831 }
4832 writeln!(stdout)?;
4833 Ok(())
4834}
4835
4836fn show_var<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4840 let (info, var_list) = match param {
4841 None => {
4842 let info = emu
4843 .get_current_program()
4844 .and_then(|(file, _, _)| emu.basic_info.get(file));
4845 (info, None)
4846 }
4847 Some(param) => {
4848 let mut iter = param.splitn(2, ' ');
4849 let label = iter.next().unwrap();
4850 let info = if label == "." {
4851 emu.get_current_program()
4852 .and_then(|(file, _, _)| emu.basic_info.get(file))
4853 } else {
4854 emu.basic_info
4855 .values()
4856 .find(|(x, _)| x.eq_ignore_ascii_case(label))
4857 };
4858 let var_list = iter.next().map(|s| s.split(',').collect::<Vec<_>>());
4859 (info, var_list)
4860 }
4861 };
4862
4863 let (key, info) = match info {
4864 None => {
4865 writeln!(stdout, "変数情報が見つかりませんでした")?;
4866 return Ok(());
4867 }
4868 Some((key, info)) => (key, info),
4869 };
4870
4871 writeln!(stdout, "{}の変数", key)?;
4872
4873 if let Some(var_list) = var_list {
4874 for name in var_list {
4875 if let Some((label, arg)) = info.label_set.argument_labels.get(name) {
4876 print_value_label(emu, stdout, key, name, label, Some(arg))?;
4877 } else if let Some((label, arg)) = info.label_set.str_argument_labels.get(name) {
4878 print_str_label(emu, stdout, key, name, label, Some(arg), false)?;
4879 } else if let Some((label, arg)) = info.label_set.arr_argument_labels.get(name) {
4880 print_arr_label(emu, stdout, key, name, label, Some(arg), false)?;
4881 } else if let Some(label) = info.label_set.bool_var_labels.get(name) {
4882 print_value_label(emu, stdout, key, name, label, None)?;
4883 } else if let Some(label) = info.label_set.int_var_labels.get(name) {
4884 print_value_label(emu, stdout, key, name, label, None)?;
4885 } else if let Some(label) = info.label_set.str_var_labels.get(name) {
4886 print_str_label(emu, stdout, key, name, label, None, false)?;
4887 } else if let Some(label) = info.label_set.bool_arr_labels.get(name) {
4888 print_arr_label(emu, stdout, key, name, label, None, false)?;
4889 } else if let Some(label) = info.label_set.int_arr_labels.get(name) {
4890 print_arr_label(emu, stdout, key, name, label, None, false)?;
4891 } else {
4892 writeln!(stdout, "{}に変数{}は存在しません", key, name)?;
4893 }
4894 }
4895 } else {
4896 let mut omit_name = None;
4897 for (name, (label, arg)) in info
4898 .label_set
4899 .argument_labels
4900 .iter()
4901 .collect::<BTreeMap<_, _>>()
4902 {
4903 print_value_label(emu, stdout, key, name, label, Some(arg))?;
4904 }
4905 for (name, (label, arg)) in info
4906 .label_set
4907 .str_argument_labels
4908 .iter()
4909 .collect::<BTreeMap<_, _>>()
4910 {
4911 print_str_label(emu, stdout, key, name, label, Some(arg), true)?;
4912 omit_name = Some(name);
4913 }
4914 for (name, (label, arg)) in info
4915 .label_set
4916 .arr_argument_labels
4917 .iter()
4918 .collect::<BTreeMap<_, _>>()
4919 {
4920 print_arr_label(emu, stdout, key, name, label, Some(arg), true)?;
4921 omit_name = Some(name);
4922 }
4923 for (name, label) in info
4924 .label_set
4925 .bool_var_labels
4926 .iter()
4927 .collect::<BTreeMap<_, _>>()
4928 {
4929 print_value_label(emu, stdout, key, name, label, None)?;
4930 }
4931 for (name, label) in info
4932 .label_set
4933 .int_var_labels
4934 .iter()
4935 .collect::<BTreeMap<_, _>>()
4936 {
4937 print_value_label(emu, stdout, key, name, label, None)?;
4938 }
4939 for (name, label) in info
4940 .label_set
4941 .str_var_labels
4942 .iter()
4943 .collect::<BTreeMap<_, _>>()
4944 {
4945 print_str_label(emu, stdout, key, name, label, None, true)?;
4946 omit_name = Some(name);
4947 }
4948 for (name, label) in info
4949 .label_set
4950 .bool_arr_labels
4951 .iter()
4952 .collect::<BTreeMap<_, _>>()
4953 {
4954 print_arr_label(emu, stdout, key, name, label, None, true)?;
4955 omit_name = Some(name);
4956 }
4957 for (name, label) in info
4958 .label_set
4959 .int_arr_labels
4960 .iter()
4961 .collect::<BTreeMap<_, _>>()
4962 {
4963 print_arr_label(emu, stdout, key, name, label, None, true)?;
4964 omit_name = Some(name);
4965 }
4966 if let Some(name) = omit_name {
4967 writeln!(stdout)?;
4968 writeln!(
4969 stdout,
4970 "※省略された値を確認するには変数名を指定した実行が必要です"
4971 )?;
4972 writeln!(stdout, " ( 例: show-var {} {} )", key, name)?;
4973 }
4974 }
4975 Ok(())
4976}
4977
4978fn show_labels<W: Write>(emu: &Emulator, stdout: &mut W, param: Option<&str>) -> io::Result<()> {
4982 let mut count = 0;
4983 if let Some(label) = param {
4984 let label = label.to_ascii_uppercase();
4985 match emu.program_labels.get(&label) {
4986 Some(adr) => {
4987 writeln!(stdout, " #{:04X} {:<8}", adr, label)?;
4988 }
4989 None => {
4990 writeln!(stdout, "プログラムエントリ{}が見つかりません", label)?;
4991 return Ok(());
4992 }
4993 }
4994 let label_map = match emu.local_labels.get(&label) {
4995 Some(map) => map,
4996 None => {
4997 writeln!(stdout, "ラベル{}にローカルラベルは設定されてません", label)?;
4998 return Ok(());
4999 }
5000 };
5001 for (adr, key) in label_map
5002 .iter()
5003 .map(|(key, adr)| (adr, key))
5004 .collect::<BTreeSet<_>>()
5005 {
5006 write!(stdout, " #{:04X} {:<8} ", adr, key,)?;
5007 count += 1;
5008 if (count & 3) == 0 {
5009 writeln!(stdout)?;
5010 }
5011 }
5012 } else {
5013 writeln!(stdout, "プログラムエントリ")?;
5014 for (adr, key) in emu
5015 .program_labels
5016 .iter()
5017 .map(|(key, adr)| (adr, key))
5018 .collect::<BTreeSet<_>>()
5019 {
5020 write!(stdout, " #{:04X} {:<8} ", adr, key)?;
5021 count += 1;
5022 if (count & 3) == 0 {
5023 writeln!(stdout)?;
5024 }
5025 }
5026 if (count & 3) != 0 {
5027 writeln!(stdout)?;
5028 }
5029 count = 0;
5030 if !emu.labels_for_debug.is_empty() {
5031 writeln!(stdout, "デバッガコマンドで生成")?;
5032 }
5033 for (adr, key) in emu
5034 .labels_for_debug
5035 .iter()
5036 .map(|(key, (adr, _))| (adr, key))
5037 .collect::<BTreeSet<_>>()
5038 {
5039 write!(stdout, " #{:04X} {:<8} ", adr, key)?;
5040 count += 1;
5041 if (count & 3) == 0 {
5042 writeln!(stdout)?;
5043 }
5044 }
5045 for (adr, key) in emu
5046 .alias_labels
5047 .iter()
5048 .map(|(key, adr)| (adr, key))
5049 .collect::<BTreeSet<_>>()
5050 {
5051 write!(stdout, " #{:04X} {:<8} ", adr, key)?;
5052 count += 1;
5053 if (count & 3) == 0 {
5054 writeln!(stdout)?;
5055 }
5056 }
5057 }
5058 if (count & 3) != 0 {
5059 writeln!(stdout)?;
5060 }
5061 Ok(())
5062}
5063
5064fn show_reg<W: Write>(emu: &Emulator, stdout: &mut W) -> io::Result<()> {
5068 writeln!(stdout)?;
5069 writeln!(
5070 stdout,
5071 "PR: #{:04X} SP: #{:04X}[{:6}] FR: (OF: {}, SF: {}, ZF: {})",
5072 emu.program_register,
5073 emu.stack_pointer,
5074 CALLSTACK_START_POSITION - emu.stack_pointer,
5075 if emu.overflow_flag { '1' } else { '0' },
5076 if emu.sign_flag { '1' } else { '0' },
5077 if emu.zero_flag { '1' } else { '0' }
5078 )?;
5079 writeln!(
5080 stdout,
5081 "GR0: #{0:04X}({0:6}) GR1: #{1:04X}({1:6}) GR2: #{2:04X}({2:6}) GR3: #{3:04X}({3:6})",
5082 emu.general_registers[0] as i16,
5083 emu.general_registers[1] as i16,
5084 emu.general_registers[2] as i16,
5085 emu.general_registers[3] as i16
5086 )?;
5087 writeln!(
5088 stdout,
5089 "GR4: #{0:04X}({0:6}) GR5: #{1:04X}({1:6}) GR6: #{2:04X}({2:6}) GR7: #{3:04X}({3:6})",
5090 emu.general_registers[4] as i16,
5091 emu.general_registers[5] as i16,
5092 emu.general_registers[6] as i16,
5093 emu.general_registers[7] as i16
5094 )?;
5095 Ok(())
5096}
5097
5098fn set_reg(emu: &mut Emulator, param: Option<&str>) -> String {
5102 let params = match param {
5103 Some(param) => param.split_whitespace().collect::<Vec<_>>(),
5104 None => return "引数がありません".to_string(),
5105 };
5106
5107 let (reg, value) = if let [reg, value] = params.as_slice() {
5108 (reg, value)
5109 } else {
5110 return "引数の数が正しくありません".to_string();
5111 };
5112
5113 match casl2::Register::try_from(*reg) {
5114 Ok(reg) => {
5115 let mut tokenizer = casl2::Tokenizer::new(value);
5116 match Value::take_single_value(emu, &mut tokenizer) {
5117 Err(msg) => msg,
5118 Ok(Value::Int(value)) => {
5119 if tokenizer.rest().trim().is_empty() {
5120 emu.general_registers[reg as usize] = value;
5121 format!("{}に#{:04X}を設定しました", reg, value)
5122 } else {
5123 "引数が不正です".to_string()
5124 }
5125 }
5126 Ok(Value::Str(s)) => {
5127 if tokenizer.rest().trim().is_empty() {
5128 match s.chars().map(jis_x_201::convert_from_char).next() {
5129 Some(ch) => {
5130 emu.general_registers[reg as usize] = ch as u16;
5131 format!("{}に{:04X}を設定しました", reg, ch as u16)
5132 }
5133 None => "空の文字定数は設定できません".to_string(),
5134 }
5135 } else {
5136 "引数が不正です".to_string()
5137 }
5138 }
5139 }
5140 }
5141 Err(_) => {
5142 if "PR".eq_ignore_ascii_case(*reg) {
5143 let mut tokenizer = casl2::Tokenizer::new(value);
5144 if let Some(adr) = tokenizer.ignore_case_hex() {
5145 emu.program_register = adr as usize;
5146 format!("PRに#{:04X}を設定しました", adr)
5147 } else {
5148 match tokenizer.extended_label() {
5149 Ok(Some(label)) => match label.get_address(emu) {
5150 Ok(adr) => {
5151 emu.program_register = adr;
5152 format!("PRに#{:04X}を設定しました", adr)
5153 }
5154 Err(msg) => msg,
5155 },
5156 Ok(None) => "引数が不正です".to_string(),
5157 Err(msg) => msg.to_string(),
5158 }
5159 }
5160 } else if "SP".eq_ignore_ascii_case(*reg) {
5161 let mut tokenizer = casl2::Tokenizer::new(value);
5162 if let Some(adr) = tokenizer.ignore_case_hex() {
5163 emu.stack_pointer = adr as usize;
5164 format!("SPに#{:04X}を設定しました", adr)
5165 } else {
5166 match tokenizer.extended_label() {
5167 Ok(Some(label)) => match label.get_address(emu) {
5168 Ok(adr) => {
5169 emu.stack_pointer = adr;
5170 format!("SPに#{:04X}を設定しました", adr)
5171 }
5172 Err(msg) => msg,
5173 },
5174 Ok(None) => "引数が不正です".to_string(),
5175 Err(msg) => msg.to_string(),
5176 }
5177 }
5178 } else if "OF".eq_ignore_ascii_case(*reg) {
5179 let flag = match *value {
5180 "0" => false,
5181 "1" => true,
5182 _ => return "引数が不正です".to_string(),
5183 };
5184 emu.overflow_flag = flag;
5185 format!("OFに{}を設定しました", value)
5186 } else if "SF".eq_ignore_ascii_case(*reg) {
5187 let flag = match *value {
5188 "0" => false,
5189 "1" => true,
5190 _ => return "引数が不正です".to_string(),
5191 };
5192 emu.sign_flag = flag;
5193 format!("SFに{}を設定しました", value)
5194 } else if "ZF".eq_ignore_ascii_case(*reg) {
5195 let flag = match *value {
5196 "0" => false,
5197 "1" => true,
5198 _ => return "引数が不正です".to_string(),
5199 };
5200 emu.zero_flag = flag;
5201 format!("ZFに{}を設定しました", value)
5202 } else {
5203 "引数が不正です".to_string()
5204 }
5205 }
5206 }
5207}
5208
5209fn show_command_help<W: Write>(cmd: Option<&str>, stdout: &mut W) -> io::Result<()> {
5213 writeln!(stdout)?;
5214 writeln!(stdout, "コマンドヘルプ")?;
5215
5216 let cmd = match cmd {
5217 Some(cmd) => cmd.to_ascii_lowercase(),
5218 None => {
5219 writeln!(
5220 stdout,
5221 r#"
5222 add-dc <LABEL> <VALUE1>[,<VALUE2>..]
5223 新しく領域を作り値を格納し先頭アドレスを示すラベルも作る。CASL2のDC相当
5224 add-ds <LABEL> <SIZE>
5225 新しく領域を確保し先頭アドレスを示すラベルを作る。CASL2のDS相当
5226 copy-mem <ADDRESS_FROM> <ADDRESS_TO> <LENGTH>
5227 メモリのデータをコピーする
5228 default-cmd [<DEBUG_COMMAND>]
5229 空行が入力されたときの挙動を設定する
5230 dump-code [<ADDRESS> [<SIZE>]]
5231 メモリをコード表示する
5232 dump-mem [<ADDRESS> [<SIZE>]]
5233 メモリダンプする
5234 fill-mem <ADDRESS> <LENGTH> <VALUE>
5235 メモリの指定アドレスから指定の長さ分を指定の値で埋める
5236 find-code <ADDRESS> <COMET2_COMMAND>
5237 指定のCOMET2コマンドを指定アドレス位置以降から探し最初に見つかったメモリ上のコードを表示する
5238 find-src <ADDRESS> <CASL2_COMMAND>
5239 指定のCASL2コマンドを指定アドレス位置以降から探し最初に見つかったコードを表示する
5240 find-value <ADDRESS> <VALUE>
5241 指定の値を指定アドレス位置以降から探し最初に見つかったものを表示する
5242 help [<COMMAND_NAME>]
5243 指定デバッガコマンドの詳細ヘルプを表示する
5244 help constant
5245 デバッガコマンドで使用する定数に関する説明を表示する
5246 list-files
5247 読み込んだソースファイルの一覧を表示する
5248 mode <MODE>
5249 指定したデバッグモードに切り替える
5250 quit
5251 テスト実行を中止しデバッガを終了する
5252 remove-breakpoint [<ADDRESS1>[,<ADDRESS2>..]]
5253 指定アドレスのブレークポイントを解除する。アドレス省略時はPRの示すアドレス
5254 reset
5255 プログラムを最初から実行しなおす。プログラムをファイルから読み込みなおし配置される
5256 restart
5257 プログラムを最初の位置から実行しなおす。メモリやGRやFRは終了時点の状態クリアされずに最後の実行状態のまま維持される
5258 run [<STEP_LIMIT>]
5259 次のブレークポイントまで実行する。ステップ制限数までにブレークポイントに到達しない場合はそこで停止する
5260 set-breakpoint [<ADDRESS1>[,<ADDRESS2>..]]
5261 指定アドレスにブレークポイントを設定する。アドレス省略時はPRの示すアドレス
5262 set-by-file <FILE_PATH>
5263 ファイルに列挙された設定系のデバッガコマンドを実行する
5264 set-label <LABEL> <ADDRESS>
5265 アドレスのエイリアスである新しいラベルを作る。
5266 set-mem <ADDRESS> <VALUE1>[,<VALUE2>..]
5267 値をメモリの指定アドレスに書き込む
5268 set-reg <REGISTER> <VALUE>
5269 値をレジスタに設定する
5270 set-start [<ADDRESS>]
5271 プログラムの開始点を変更する(restart時に影響)。省略した場合は最初の開始点に戻す
5272 show-labels [<PROGRAM_ENTRY>]
5273 ラベルの一覧とアドレスを表示する。PROGRAM_ENTRYを指定した場合はローカルラベルを表示する
5274 show-mem <ADDRESS> [<LENGTH>] [<TYPE>]
5275 メモリの指定アドレスから指定の長さ分の領域の各値を列挙する
5276 show-mem-stat <ADDRESS> [<LENGTH>]
5277 メモリの指定アドレスから指定の長さ分の領域の統計情報ぽいものを表示する
5278 show-reg
5279 各レジスタの現在の値を表示する
5280 show-src [<ADDRESS> [<LENGTH>]]
5281 指定したアドレス位置から指定長さ分の範囲にあるコードを表示する
5282 show-state
5283 直近の実行(run,skip,step)の結果を再表示する
5284 show-var [<BASIC_PROGRAM_ENTRY> [<VAR_NAME1>[,<VAR_NAME2>..]]]
5285 指定したBASICプログラムの変数名と対応するラベルとアドレスと値を表示する
5286 skip [<STEP_LIMIT>]
5287 現在のサブルーチンのRETまで実行する。ステップ制限数までにRETに到達しない場合はそこで停止する
5288 s [<STEP_COUNT>]
5289 step [<STEP_COUNT>]
5290 指定ステップ数だけ実行する。STEP_COUNT省略時は1ステップだけ実行する
5291 write-code <ADDRESS> <CASL2_COMMAND>
5292 アドレス位置に指定のCASL2コマンドを書き込む
5293"#
5294 )?;
5295 return Ok(());
5296 }
5297 };
5298
5299 if "constant" == cmd {
5300 writeln!(
5301 stdout,
5302 r#"
5303 10進定数
5304 -32768 ~ 32767
5305 16進定数
5306 #0000 ~ #FFFF
5307 文字定数
5308 文字・文字列を引用符で囲ったもの(引用符を2つ続けると引用符1文字の文字として扱う)
5309 例: 'X'
5310 'Abc123'
5311 'Let''s go!'
5312 アドレス定数 (当デバッガの拡張仕様となっている)
5313 ラベル
5314 グローバルラベル (各プログラムのエントリラベル、デバッガコマンドで定義したラベル)
5315 例: MAIN
5316 ラベル:ラベル
5317 ローカルラベル (各プログラムの内部のラベル)。 プログラムのエントリラベル:内部ラベル で表記
5318 例: MAIN:I001
5319 LIB:MEM
5320 @レジスタ
5321 レジスタ(GR*,PR,SP)の値をアドレスとする
5322 例: @GR3
5323 @PR
5324 (アドレス定数)
5325 (16進定数)
5326 アドレス定数または16進定数の示すアドレスから読み込んだ値をアドレスとする
5327 例: (MAIN:MEM)
5328 (#1234)
5329 アドレス定数+アドレス定数
5330 アドレス定数+16進定数
5331 アドレス定数+10進数
5332 2つのアドレス定数または16進定数の和をアドレスとする(オーバーフローに注意)
5333 例: MAIN+@GR1
5334 (MAIN:MEM)+#0101
5335 MAIN:FOO+10
5336 アドレス定数-アドレス定数
5337 アドレス定数-16進定数
5338 アドレス定数-10進数
5339 16進定数-アドレス定数
5340 2つのアドレス定数または16進定数の差をアドレスとする(オーバーフローに注意)
5341 例: LIB:S005-LIB:B001
5342 MAIN:J004-#0010
5343 MAIN:FOO-3
5344 #FFFF-@SP
5345 リテラル
5346 10進定数、16進定数、文字定数の頭に=を付けて指定
5347 領域を確保し値を格納の後そのアドレスを返す
5348 例: =123
5349 =#ABCD
5350 ='XYZ'
5351"#
5352 )?;
5353 return Ok(());
5354 }
5355
5356 let description = match cmd.as_str() {
5357 "add-dc" => {
5358 r#"
5359 add-dc <LABEL> <VALUE1>[,<VALUE2>..]
5360 新しく領域を作り値を格納し先頭アドレスを示すラベルも作る。CASL2のDC相当
5361 LABEL .. CASL2のラベル仕様に従う。デバッガで未使用のラベル名を指定する
5362 VALUE* .. 10進定数、16進定数、文字定数、アドレス定数、リテラルを指定できる
5363"#
5364 }
5365 "add-ds" => {
5366 r#"
5367 add-ds <LABEL> <SIZE>
5368 新しく領域を確保し先頭アドレスを示すラベルを作る。CASL2のDS相当
5369 LABEL .. CASL2のラベル仕様に従う。デバッガで未使用のラベル名を指定する
5370 SIZE .. 確保する領域のサイズ(単位は語)。正の10進数か16進定数で指定する
5371"#
5372 }
5373 "copy-mem" => {
5374 r#"
5375 copy-mem <ADDRESS_FROM> <ADDRESS_TO> <LENGTH>
5376 メモリのデータをコピーする
5377 ADDRESS_FROM .. コピー元の先頭アドレス。16進定数かアドレス定数を指定できる
5378 ADDRESS_TO .. コピー先の先頭アドレス。16進定数かアドレス定数を指定できる
5379 LENGTH .. コピーするサイズ(単位は語)。正の10進数か16進定数で指定する
5380"#
5381 }
5382 "default-cmd" => {
5383 r#"
5384 default-cmd [<DEBUG_COMMAND>]
5385 空行が入力されたときの挙動を設定する
5386 DEBUG_COMMAND .. デバッガコマンドとその引数を空行入力時のコマンドとして割り当てる
5387 未指定の場合は現在の割り当てコマンドを表示する
5388 default-cmd none
5389 空行が入力されたときのコマンド割り当てを解除する
5390"#
5391 }
5392 "dump-code" => {
5393 r#"
5394 dump-code [<ADDRESS> [<SIZE>]]
5395 メモリをコード表示する
5396 各メモリ位置をCOMET2命令として解釈できる箇所をコード表示していく
5397 ADDRESS .. 表示対象の先頭アドレス。16進定数かアドレス定数を指定する
5398 SIZE .. 表示対象のサイズ(単位は語)。正の10進数か16進定数で指定する
5399"#
5400 }
5401 "dump-mem" => {
5402 r#"
5403 dump-mem [<ADDRESS> [<SIZE>]]
5404 メモリダンプする
5405 各メモリ位置の16進定数表記と8bitごとを文字表記とを表示する
5406 ADDRESS .. 表示対象の先頭アドレス。16進定数かアドレス定数を指定する
5407 SIZE .. 表示対象のサイズ(単位は語)。正の10進数か16進定数で指定する
5408"#
5409 }
5410 "fill-mem" => {
5411 r#"
5412 fill-mem <ADDRESS> <LENGTH> <VALUE>
5413 メモリの指定アドレスから指定の長さ分を指定の値で埋める
5414 ADDRESS .. 16進定数,アドレス定数
5415 LENGTH .. 正の10進数,16進定数
5416 VALUE .. 10進定数,16進定数,文字定数(1文字),アドレス定数,リテラル
5417"#
5418 }
5419 "find-code" => {
5420 r#"
5421 find-code <ADDRESS> <COMET2_COMMAND>
5422 指定のCOMET2コマンドを指定アドレス位置以降から探し最初に見つかったメモリ上のコードを表示する
5423 ADDRESS .. 検索開始位置のアドレス。16進定数かアドレス定数で指定
5424 COMET2_COMMAND .. COMET2のコマンドをCASL2形式で指定する
5425 (アドレス定数にはデバッガの拡張された形式が使用可能)
5426 例:
5427 find-code MAIN LAD GR1,MAIN:FOO,GR2
5428"#
5429 }
5430 "find-src" => {
5431 r#"
5432 find-src <ADDRESS> <CASL2_COMMAND>
5433 指定のCASL2コマンドを指定アドレス位置以降から探し最初に見つかったコードを表示する
5434 CASL2ソースまたはBASICから変換されたCASL2ソースから検索する
5435 ※ソースは整形されている場合がありshow-srcコマンドで確認できる
5436 ADDRESS .. 検索開始位置のアドレス。16進定数かアドレス定数で指定
5437 COMET2_COMMAND .. CASL2のコマンドをソースに記載されている形で指定する
5438 例:
5439 find-src MAIN LAD GR1,FOO,GR2
5440"#
5441 }
5442 "find-value" => {
5443 r#"
5444 find-value <ADDRESS> <VALUE>
5445 指定の値をメモリの指定アドレス位置から探し最初に見つかった位置を表示する
5446 ADDRESS .. 検索開始位置のアドレス。16進定数かアドレス定数で指定
5447 VALUE .. 検索する値。10進定数、16進定数、文字定数、アドレス定数を指定できる
5448"#
5449 }
5450 "help" => {
5451 r#"
5452 help [<COMMAND_NAME>]
5453 指定デバッガコマンドの詳細ヘルプを表示する
5454 COMMAND_NAME .. デバッガコマンド名を指定する
5455 省略時は全てのコマンドの簡易ヘルプを表示する
5456 help constant
5457 デバッガコマンドで使用する定数に関する説明を表示する
5458"#
5459 }
5460 "list-files" => {
5461 r#"
5462 list-files
5463 読み込んだソースファイルの一覧を表示する
5464 ソースファイルとラベルの対応を表示する
5465"#
5466 }
5467 "mode" => {
5468 r#"
5469 mode <MODE>
5470 指定したデバッグモードに切り替える(BASICデバッグモードで起動した場合にのみ有効)
5471 MODE .. BASICかCASL2を指定して切り替える
5472"#
5473 }
5474 "quit" => {
5475 r#"
5476 quit
5477 テスト実行を中止しデバッガを終了する
5478"#
5479 }
5480 "remove-breakpoint" => {
5481 r#"
5482 remove-breakpoint [<ADDRESS1>[,<ADDRESS2>..]]
5483 指定アドレスのブレークポイントを解除する。アドレス省略時はPRの示すアドレス
5484 ADDRESS* .. 解除対象のアドレス。16進定数かアドレス定数で指定
5485"#
5486 }
5487 "reset" => {
5488 r#"
5489 reset
5490 プログラムを最初から実行しなおす。プログラムをファイルから読み込みなおし配置される
5491"#
5492 }
5493 "restart" => {
5494 r#"
5495 restart
5496 プログラムを最初の位置から実行しなおす。メモリやGRやFRは終了時点の状態クリアされずに最後の実行状態のまま維持される
5497 set-startコマンドで指定されている場合はそのプログラムからスタートとなる
5498"#
5499 }
5500 "run" => {
5501 r#"
5502 run [<STEP_LIMIT>]
5503 次のブレークポイントまで実行する。ステップ制限数までにブレークポイントに到達しない場合はそこで停止する
5504 STEP_LIMIT .. ステップ制限数。正の10進数で指定する(最大値は18446744073709551615)
5505 省略した場合は 100000000
5506"#
5507 }
5508 "set-breakpoint" => {
5509 r#"
5510 set-breakpoint [<ADDRESS1>[,<ADDRESS2>..]]
5511 指定アドレスにブレークポイントを設定する。アドレス省略時はPRの示すアドレス
5512 ADDRESS* .. 解除対象のアドレス。16進定数かアドレス定数で指定
5513"#
5514 }
5515 "set-by-file" => {
5516 r#"
5517 set-by-file <FILE_PATH>
5518 ファイルに列挙された設定系のデバッガコマンドを実行する
5519 1行につき1コマンドを配置できる
5520 空行は無視される
5521 ファイルに指定できないデバッガコマンド:
5522 quit reset restart run set-by-file skip s step
5523"#
5524 }
5525 "set-label" => {
5526 r#"
5527 set-label <LABEL> <ADDRESS>
5528 アドレスのエイリアスである新しいラベルを作る。
5529 LABEL .. CASL2のラベル仕様に従う。デバッガで未使用のラベル名を指定する
5530 ADDRESS .. エイリアス対象のアドレス。16進定数かアドレス定数で指定
5531"#
5532 }
5533 "set-mem" => {
5534 r#"
5535 set-mem <ADDRESS> <VALUE1>[,<VALUE2>..]
5536 値をメモリの指定アドレスに書き込む
5537 値を複数列挙した場合はアドレスから連続した領域に書き込まれる
5538 値が文字定数の場合は文字数分の連続した領域に書き込まれる
5539 ADDRESS .. 16進定数,アドレス定数
5540 VALUE* .. 10進定数,16進定数,文字定数,アドレス定数,リテラル
5541"#
5542 }
5543 "set-reg" => {
5544 r#"
5545 set-reg <REGISTER> <VALUE>
5546 値をレジスタに設定する
5547
5548 REGISTER .. GR0~GR7
5549 VALUE .. 10進定数,16進定数,文字定数(1文字),アドレス定数,リテラル
5550
5551 REGISTER .. OF,SF,ZF
5552 VALUE .. 0,1
5553
5554 REGISTER .. PR,SP
5555 VALUE .. 16進定数,アドレス定数
5556"#
5557 }
5558 "set-start" => {
5559 r#"
5560 set-start [<ADDRESS>]
5561 プログラムの開始点を変更する(restart時に影響)。省略した場合は最初の開始点に戻す
5562 ADDRESS .. プログラム開始点のアドレス。16進定数かアドレス定数で指定
5563"#
5564 }
5565 "show-labels" => {
5566 r#"
5567 show-labels [<PROGRAM_ENTRY>]
5568 ラベルの一覧とアドレスを表示する。PROGRAM_ENTRYを指定した場合はローカルラベルを表示する
5569 PROGRAM_ENTRY .. プログラムエントリラベルを指定
5570"#
5571 }
5572 "show-mem" => {
5573 r#"
5574 show-mem <ADDRESS> [<LENGTH>] [<TYPE>]
5575 メモリの指定アドレスから指定の長さ分の領域の各値を列挙する
5576 ADDRESS .. 対象の先頭アドレス。16進定数かアドレス定数で指定
5577 LENGTH .. 表示対象のメモリのサイズ(単位は語)。正の10進数か16進定数で指定
5578 省略した場合は1語
5579 TYPE .. 指定するとBASICの型に合わせた内容表示になる
5580 指定できるのはboolean,integer,stringの3種類
5581 省略した場合は16進定数として表示される
5582"#
5583 }
5584 "show-mem-stat" => {
5585 r#"
5586 show-mem-stat <ADDRESS> [<LENGTH>]
5587 メモリの指定アドレスから指定の長さ分の領域の統計情報ぽいものを表示する
5588 表示されるのは書込回数 読込回数 実行回数となる
5589 書込回数 .. ST命令やOUT命令で指定されたアドレスに書き込まれた回数
5590 読込回数 .. LD命令などで指定されたアドレスから読み込まれた回数
5591 ※アドレス指定で読み込む命令による読み込み全てが対象
5592 実行回数 .. PRで指定されたアドレスが実行された回数
5593 ADDRESS .. 対象の先頭アドレス。16進定数かアドレス定数で指定
5594 LENGTH .. 表示対象のメモリのサイズ(単位は語)。正の10進数か16進定数で指定
5595 省略した場合は1語
5596"#
5597 }
5598 "show-reg" => {
5599 r#"
5600 show-reg
5601 各レジスタの現在の値を表示する
5602 GR0~GR7,PR,SP,FR(ZF,SF,OF)が表示対象。
5603 GR0~GR7は10進定数と16進定数の2つの表現で表示される
5604 PR,SPは16進定数として表示される
5605 ZF,SF,OFはビットとして0または1が表示される
5606"#
5607 }
5608 "show-src" => {
5609 r#"
5610 show-src [<ADDRESS> [<LENGTH>]]
5611 指定したアドレス位置から指定長さ分の範囲にあるコードを表示する
5612 CASL2ソースまたはBASICから変換されたCASL2ソースを表示する(※ソースは整形されている場合がある)
5613 ADDRESS .. 対象の先頭アドレス。16進定数かアドレス定数で指定
5614 LENGTH .. 表示対象のメモリのサイズ(単位は語)。正の10進数か16進定数で指定
5615 省略した場合は40語分
5616"#
5617 }
5618 "show-state" => {
5619 r#"
5620 show-state
5621 直近の実行(run,skip,step)の結果を再表示する
5622 ※レジスタ情報は表示されない
5623"#
5624 }
5625 "show-var" => {
5626 r#"
5627 show-var [<BASIC_PROGRAM_ENTRY> [<VAR_NAME1>[,<VAR_NAME2>..]]]
5628 指定したBASICプログラムの変数名と対応するラベルとアドレスと値を表示する
5629 BASIC_PROGRAM_ENTRY .. BASICソースに指定されているプログラムエントリラベル
5630 VAR_NAME* .. BASICソースに含まれる変数名(注意:大文字小文字は区別される)
5631"#
5632 }
5633 "skip" => {
5634 r#"
5635 skip [<STEP_LIMIT>]
5636 現在のサブルーチンのRETまで実行する。ステップ制限数までにRETに到達しない場合はそこで停止する
5637 STEP_LIMIT .. ステップ制限数。正の10進数で指定する(最大値は18446744073709551615)
5638 省略した場合は 100000000
5639"#
5640 }
5641 "s" | "step" => {
5642 r#"
5643 s [<STEP_COUNT>]
5644 step [<STEP_COUNT>]
5645 指定ステップ数だけ実行する。STEP_COUNT省略時は1ステップだけ実行する
5646 STEP_COUNT .. ステップ数。正の10進数で指定する(最大値は18446744073709551615)
5647 省略した場合は 1
5648"#
5649 }
5650 "write-code" => {
5651 r#"
5652 write-code <ADDRESS> <CASL2_COMMAND>
5653 アドレス位置に指定のCASL2コマンドを書き込む
5654 ADDRESS .. 対象の先頭アドレス。16進定数かアドレス定数で指定
5655 CASL2_COMMAND .. 書き込むCASL2コマンド
5656 アドレス定数にはデバッガの拡張形式が使用可能
5657 IN,OUT,RPUSH,RPOPは展開されて複数の命令が書き込まれる
5658 START,END,DSは指定できない
5659 例:
5660 add-ds FOO 6
5661 write-code FOO LD GR1,MAIN:X
5662 write-code FOO+#0002 LD GR2,MAIN:Y
5663 write-code FOO+#0004 ADDL GR1,GR2
5664 write-code FOO+#0005 RET
5665"#
5666 }
5667 _ => "コマンド名が正しくありません",
5668 };
5669
5670 writeln!(stdout, "{}", description)?;
5671
5672 Ok(())
5673}
5674
5675fn show_command_help_for_basic<W: Write>(cmd: Option<&str>, stdout: &mut W) -> io::Result<()> {
5679 writeln!(stdout)?;
5680 writeln!(stdout, "コマンドヘルプ")?;
5681
5682 let cmd = match cmd {
5683 Some(cmd) => cmd.to_ascii_lowercase(),
5684 None => {
5685 writeln!(
5686 stdout,
5687 r#"
5688 default-cmd [<DEBUG_COMMAND>]
5689 空行が入力されたときの挙動を設定する
5690 fill-arr <VAR_NAME> <VALUE>
5691 現在地点のプログラムの配列変数の全ての要素を指定の値で埋める。値はBASICリテラルのみ
5692 help [<COMMAND_NAME>]
5693 指定デバッガコマンドの詳細ヘルプを表示する
5694 list-files
5695 読み込んだソースファイルの一覧を表示する
5696 mode <MODE>
5697 指定したモードに切り替える
5698 quit
5699 テスト実行を中止しデバッガを終了する
5700 remove-breakpoint <BASIC_PROGRAM_ENTRY> <STATEMENT_ID1>[,<STATEMENT_ID2>..]
5701 指定したBASICプログラムからブレークポイントを解除する
5702 reset
5703 プログラムを最初から実行しなおす。プログラムをファイルから読み込みなおし配置される
5704 restart
5705 プログラムを最初の位置から実行しなおす。メモリの状態はクリアされずに最後の実行状態のまま維持される
5706 run [<BASIC_STEP_LIMIT> [<COMET2_STEP_LIMIT>]]
5707 次のブレークポイントまで実行する
5708 set-breakpoint <BASIC_PROGRAM_ENTRY> <STATEMENT_ID1>[,<STATEMENT_ID2>..]
5709 指定したBASICプログラムにブレークポイントを設定する
5710 set-by-file <FILE_PATH>
5711 ファイルに列挙された設定系のデバッガコマンドを実行する
5712 set-elem <VAR_NAME> <INDEX> <VALUE>
5713 現在地点のプログラムの配列変数の要素に値を設定する。値はBASICリテラルのみ
5714 set-len <STR_VAR_NAME> <LENGTH>
5715 現在地点のプログラムの文字列変数の長さ情報のみを書き換える
5716 set-var <VAR_NAME> <VALUE1>[,<VALUE2>..]
5717 現在地点のプログラムの変数に値を設定する。値はBASICリテラルのみ
5718 show-execute-stat [<BASIC_PROGRAM_ENTRY>]
5719 指定したBASICプログラムのコード実行の統計情報ぽいものを表示する
5720 show-src [<BASIC_PROGRAM_ENTRY>]
5721 指定したBASICプログラムのコードを表示する
5722 show-state
5723 直近の実行(run,skip,step)の結果を再表示する
5724 show-var [<BASIC_PROGRAM_ENTRY> [<VAR_NAME1>[,<VAR_NAME2>..]]]
5725 指定したBASICプログラムの変数名を表示する。可能な場合は値も表示する
5726 skip [<BASIC_STEP_LIMIT> [<COMET2_STEP_LIMIT>]]
5727 現在地点のサブルーチンを抜けるまで実行する
5728 s [<BASIC_STEP_COUNT> [<COMET2_STEP_LIMIT>]]
5729 step [<BASIC_STEP_COUNT> [<COMET2_STEP_LIMIT>]]
5730 指定ステップ数だけ実行する。BASIC_STEP_COUNT省略時は1ステップだけ実行する
5731"#
5732 )?;
5733 return Ok(());
5734 }
5735 };
5736
5737 let description = match cmd.as_str() {
5738 "default-cmd" => {
5739 r#"
5740 default-cmd [<DEBUG_COMMAND>]
5741 空行が入力されたときの挙動を設定する
5742 DEBUG_COMMAND .. デバッガコマンドとその引数を空行入力時のコマンドとして割り当てる
5743 未指定の場合は現在の割り当てコマンドを表示する
5744 default-cmd none
5745 空行が入力されたときのコマンド割り当てを解除する
5746"#
5747 }
5748 "fill-arr" => {
5749 r#"
5750 fill-arr <VAR_NAME> <VALUE>
5751 現在地点のプログラムの配列変数の全ての要素を指定の値で埋める。値はBASICリテラルのみ
5752 文字列を指定した場合は文字列のその時点の長さ分だけ埋める
5753 プログラムが(initialize)に到達してない場合は設定できない
5754 VAR_NAME .. 固定長配列の変数名または文字列の変数名(注意:大文字小文字は区別される)
5755 VALUE .. 配列に対応する型のBASICリテラル値(真理値/整数)
5756"#
5757 }
5758 "help" => {
5759 r#"
5760 help [<COMMAND_NAME>]
5761 指定デバッガコマンドの詳細ヘルプを表示する
5762 COMMAND_NAME .. デバッガコマンド名を指定する
5763 省略時は全てのコマンドの簡易ヘルプを表示する
5764"#
5765 }
5766 "list-files" => {
5767 r#"
5768 list-files
5769 読み込んだソースファイルの一覧を表示する
5770 ソースファイルとラベルの対応を表示する
5771"#
5772 }
5773 "mode" => {
5774 r#"
5775 mode <MODE>
5776 指定したデバッグモードに切り替える(BASICデバッグモードで起動した場合にのみ有効)
5777 MODE .. BASICかCASL2を指定して切り替える
5778"#
5779 }
5780 "quit" => {
5781 r#"
5782 quit
5783 テスト実行を中止しデバッガを終了する
5784"#
5785 }
5786 "remove-breakpoint" => {
5787 r#"
5788 remove-breakpoint <BASIC_PROGRAM_ENTRY> <STATEMENT_ID1>[,<STATEMENT_ID2>..]
5789 指定したBASICプログラムからブレークポイントを解除する
5790 BASIC_PROGRAM_ENTRY .. BASICソースに指定されているプログラムエントリラベル
5791 STATEMENT_ID* .. BASICソースのステートメントID
5792 IDはshow-srcコマンドで確認できる
5793"#
5794 }
5795 "reset" => {
5796 r#"
5797 reset
5798 プログラムを最初から実行しなおす。プログラムをファイルから読み込みなおし配置される
5799"#
5800 }
5801 "restart" => {
5802 r#"
5803 restart
5804 プログラムを最初の位置から実行しなおす。メモリの状態はクリアされずに最後の実行状態のまま維持される
5805 set-startコマンドで指定されている場合はそのプログラムからスタートとなる
5806"#
5807 }
5808 "run" => {
5809 r#"
5810 run [<BASIC_STEP_LIMIT> [<COMET2_STEP_LIMIT>]]
5811 次のブレークポイントまで実行する
5812 BASIC_STEP_LIMIT .. BASICステートメントのステップ数制限
5813 省略した場合は 100000000
5814 COMET2_STEP_LIMIT .. COMET2命令のステップ数制限
5815 省略した場合は 1000000000000
5816"#
5817 }
5818 "set-breakpoint" => {
5819 r#"
5820 set-breakpoint <BASIC_PROGRAM_ENTRY> <STATEMENT_ID1>[,<STATEMENT_ID2>..]
5821 指定したBASICプログラムにブレークポイントを設定する
5822 BASIC_PROGRAM_ENTRY .. BASICソースに指定されているプログラムエントリラベル
5823 STATEMENT_ID* .. BASICソースのステートメントID
5824 IDはshow-srcコマンドで確認できる
5825"#
5826 }
5827 "set-by-file" => {
5828 r#"
5829 set-by-file <FILE_PATH>
5830 ファイルに列挙された設定系のデバッガコマンドを実行する
5831 1行につき1コマンドを配置できる
5832 空行は無視される
5833 ファイルに指定できないデバッガコマンド:
5834 quit reset restart run set-by-file skip s step
5835"#
5836 }
5837 "set-elem" => {
5838 r#"
5839 set-elem <VAR_NAME> <INDEX> <VALUE>
5840 現在地点のプログラムの配列変数の要素に値を設定する。値はBASICリテラルのみ
5841 プログラムが(initialize)に到達してない場合は設定できない
5842 VAR_NAME .. 固定長配列の変数名または文字列の変数名(注意:大文字小文字は区別される)
5843 INDEX .. 値を設定する配列・文字列の位置。10進数で指定。長さ以上の値は指定できない
5844 VALUE .. 配列に対応する型のBASICリテラル値(真理値/整数)
5845"#
5846 }
5847 "set-len" => {
5848 r#"
5849 set-len <STR_VAR_NAME> <LENGTH>
5850 現在地点のプログラムの文字列変数の長さ情報のみを書き換える
5851 プログラムが(initialize)に到達してない場合は設定できない
5852 STR_VAR_NAME .. 文字列の変数名(注意:大文字小文字は区別される)
5853 LENGTH .. 文字列の長さ。0以上256以下の10進数で指定
5854"#
5855 }
5856 "set-var" => {
5857 r#"
5858 set-var <VAR_NAME> <VALUE1>[,<VALUE2>..]
5859 現在地点のプログラムの変数に値を設定する。値はBASICリテラルのみ
5860 プログラムが(initialize)に到達してない場合は変数に値を設定できない
5861 プログラムのスタート地点の場合は引数に値の設定が可能(必要に応じてメモリ確保がされる)
5862 VAR_NAME .. 変数名(注意:大文字小文字は区別される)
5863 VALUE* .. 変数に代入する値や文字列。BASICリテラル
5864 配列の場合はカンマ区切りで配列長分の値を並べる必要がある
5865"#
5866 }
5867 "show-execute-stat" => {
5868 r#"
5869 show-execute-stat [<BASIC_PROGRAM_ENTRY>]
5870 指定したBASICプログラムのコード実行の統計情報ぽいものを表示する
5871 各ステートメントの到達回数が表示される
5872 BASIC_PROGRAM_ENTRY .. BASICソースに指定されているプログラムエントリラベル
5873"#
5874 }
5875 "show-src" => {
5876 r#"
5877 show-src [<BASIC_PROGRAM_ENTRY>]
5878 指定したBASICプログラムのコードを表示する
5879 入力されたファイルのソースコードではなく、
5880 CASL2ソースへ変換後に埋め込まれた情報から復元したコードとなる
5881 BASIC_PROGRAM_ENTRY .. BASICソースに指定されているプログラムエントリラベル
5882"#
5883 }
5884 "show-state" => {
5885 r#"
5886 show-state
5887 直近の実行(run,skip,step)の結果を再表示する
5888 ※変数の情報は表示されない
5889"#
5890 }
5891 "show-var" => {
5892 r#"
5893 show-var [<BASIC_PROGRAM_ENTRY> [<VAR_NAME1>[,<VAR_NAME2>..]]]
5894 指定したBASICプログラムの変数名を表示する。可能な場合は値も表示する
5895 値が表示されるのは実行状態で(initialize)に到達しているプログラムが対象となる
5896 BASIC_PROGRAM_ENTRY .. BASICソースに指定されているプログラムエントリラベル
5897 VAR_NAME* .. 対象プログラムの変数名(注意:大文字小文字は区別される)
5898"#
5899 }
5900 "skip" => {
5901 r#"
5902 skip [<BASIC_STEP_LIMIT> [<COMET2_STEP_LIMIT>]]
5903 現在地点のサブルーチンを抜けるまで実行する
5904 BASIC_STEP_LIMIT .. BASICステートメントのステップ数制限
5905 省略した場合は 100000000
5906 COMET2_STEP_LIMIT .. COMET2命令のステップ数制限
5907 省略した場合は 1000000000000
5908"#
5909 }
5910 "s" | "step" => {
5911 r#"
5912 s [<BASIC_STEP_COUNT> [<COMET2_STEP_LIMIT>]]
5913 step [<BASIC_STEP_COUNT> [<COMET2_STEP_LIMIT>]]
5914 指定ステップ数だけ実行する。BASIC_STEP_COUNT省略時は1ステップだけ実行する
5915 BASIC_STEP_COUNT .. BASICステートメントを実行するステップ数
5916 省略した場合は 1
5917 COMET2_STEP_LIMIT .. COMET2命令のステップ数制限
5918 省略した場合は 1000000000000
5919"#
5920 }
5921 _ => "コマンド名が正しくありません",
5922 };
5923
5924 writeln!(stdout, "{}", description)?;
5925
5926 Ok(())
5927}