Skip to main content

b2c2_debugger/
lib.rs

1// b2c2-debugger crate
2// author: Leonardone @ NEETSDKASU
3
4use 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        // 未解決ラベルを解決する処理を入れる
122        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            // 未解決ラベルを解決する処理を入れる
173            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            // 未解決ラベルを解決する処理を入れる
320            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
387//
388// ブレークポイントまで実行実行 (BASIC)
389//
390fn 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
452//
453// ステップ実行 (BASIC)
454//
455fn 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
498//
499// スキップ (BASIC)
500//
501fn 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
554//
555// ステップ実行 (CASL2)
556//
557fn 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
585//
586// ブレークポイントまで実行 (CASL2)
587//
588fn 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
643//
644// スキップ (CASL2)
645//
646fn 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
692//
693// 対話処理 (BASIC)
694//
695fn 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
958//
959// 対話処理 (CASL2)
960//
961fn 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
1198//
1199// show-var [<BASIC_PROGRAM_ENTRY> [<VAR_NAME1>[,<VAR_NAME2>..]]]
1200//
1201fn 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
1683//
1684// set-len <STR_VAR_NAME> <LENGTH>
1685//
1686fn 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
1761//
1762// fill-arr <VAR_NAME> <VALUE>
1763//
1764fn 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
1896//
1897// set-elem <VAR_NAME> <INDEX> <VALUE>
1898//
1899fn 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
2059//
2060// set-var <VAR_NAME> <VALUE1>[,<VALUE2>..]
2061//
2062fn 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
2444//
2445// remove-breakpoint <BASIC_PROGRAM_ENTRY> <STATEMENT_ID1>[,<STATEMENT_ID2>..]
2446// set-breakpoint <BASIC_PROGRAM_ENTRY> <STATEMENT_ID1>[,<STATEMENT_ID2>..]
2447//
2448fn 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
2555//
2556// show-execute-stat [<BASIC_PROGRAM_ENTRY>]
2557//
2558fn 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
2641//
2642// show-src [<BASIC_PROGRAM_ENTRY>] (BASIC)
2643//
2644fn 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
2710//
2711// show-state (BASIC)
2712//
2713fn 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
2844//
2845// show-state (CASL2)
2846//
2847fn 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
2981//
2982// set-by-file <FILE_PATH>
2983//
2984fn 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
3190//
3191// write-code <ADDRESS> <CASL2_COMMAND>
3192//
3193fn 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(&param);
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(&param);
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
3473//
3474// show-src [<ADDRESS> [<LENGTH>]]
3475//
3476fn 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
3550//
3551// show-mem-stat <ADDRESS> [<LENGTH>]
3552//
3553fn 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
3604//
3605// show-mem <ADDRESS> [<LENGTH>] [<TYPE>]
3606//
3607fn 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                    // token は len
3628                    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                    // token は len OR type
3643                    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
3760//
3761// find-src <ADDRESS> <CASL2_COMMAND>
3762//
3763fn 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
3838//
3839// fill-mem <ADDRESS> <LENGTH> <VALUE>
3840//
3841fn 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
3932//
3933// set-label <LABEL> <ADDRESS>
3934//
3935fn 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
3983//
3984// find-value <START_ADDRESS> <VALUE>
3985//
3986fn 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
4060//
4061// find-code <START_ADDRESS> <COMET2_COMMAND>
4062//
4063fn 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
4196//
4197// copy-mem <ADDRESS_FROM> <ADDRESS_TO> <LENGTH>
4198//
4199fn 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
4269//
4270// add-ds <LABEL> <SIZE>
4271//
4272fn 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
4342//
4343// add-dc <LABEL> <VALUE1>[,<VALUE2>..]
4344//
4345fn 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
4438//
4439// set-mem <ADDRESS> <VALUE1>[,<VALUE2>..]
4440//
4441fn 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
4533//
4534// set-breakpoint <ADDRESS1>[,<ADDRESS2>..]
4535//
4536fn 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(&param);
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
4604//
4605// dump-mem <ADDRESS> [<SIZE>]
4606//
4607fn 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
4726//
4727// dump-code <ADDRESS> [<SIZE>]
4728//
4729fn 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
4815//
4816// list-files
4817//
4818fn 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
4836//
4837// show-var [<BASIC_PROGRAM_ENTRY> [<VAR_NAME1>[,<VAR_NAME2>..]]]
4838//
4839fn 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
4978//
4979// show-labels [<LABEL>]
4980//
4981fn 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
5064//
5065// show-reg
5066//
5067fn 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
5098//
5099// set-reg <REG> <VALUE>
5100//
5101fn 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
5209//
5210// help [<TARGET>] (CASL2)
5211//
5212fn 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
5675//
5676// help [<TARGET>] (BASIC)
5677//
5678fn 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}