diff --git a/.gitignore b/.gitignore
index 8774c22..5265a51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
/target
/testcases
-/output
\ No newline at end of file
+/output
+*.pyc
+*.zip
\ No newline at end of file
diff --git a/tests/std.c b/tests/std.c
new file mode 100644
index 0000000..b2da05c
--- /dev/null
+++ b/tests/std.c
@@ -0,0 +1,121 @@
+///
+/// @file std.c
+/// @brief 外部或内置函数实现
+/// @author zenglj (zenglj@live.com)
+/// @version 1.0
+/// @date 2024-09-29
+///
+/// @copyright Copyright (c) 2024
+///
+/// @par 修改日志:
+///
+/// | Date | Version | Author | Description
+/// |
|---|
| 2024-09-29 | 1.0 | zenglj | 新做
+/// |
+///
+#include
+#include
+
+int getint()
+{
+ int d;
+
+ scanf("%d", &d);
+
+ return d;
+}
+
+int getch()
+{
+ char d;
+
+ scanf("%c", &d);
+
+ return d;
+}
+
+int getarray(int a[])
+{
+ int n, i;
+
+ // 获取元素个数
+ scanf("%d",&n);
+
+ // 获取元素内容
+ for(i = 0; i < n; ++i) {
+ scanf("%d",&a[i]);
+ }
+
+ return n;
+}
+
+void putint(int k)
+{
+ printf("%d", k);
+}
+
+void putch(int c)
+{
+ printf("%c", (char)c);
+}
+
+void putarray(int n, int * d)
+{
+ int k;
+
+ // 输出元素个数
+ printf("%d:", n);
+
+ // 输出元素内容,空格分割
+ for(k = 0; k < n; k ++) {
+ printf(" %d", d[k]);
+ }
+
+ // 输出换行符
+ printf("\n");
+}
+
+void putstr(char * str)
+{
+ printf("%s", str);
+}
+
+float getfloat()
+{
+ float n;
+ scanf("%a", &n);
+ return n;
+}
+
+int getfarray(float a[])
+{
+ int n;
+ scanf("%d", &n);
+ for (int i = 0; i < n; i++) {
+ scanf("%a", &a[i]);
+ }
+ return n;
+}
+
+void putfloat(float a)
+{
+ printf("%a", a);
+}
+
+void putfarray(int n, float a[])
+{
+ printf("%d:", n);
+ for (int i = 0; i < n; i++) {
+ printf(" %a", a[i]);
+ }
+ printf("\n");
+}
+
+void putf(char a[], ...)
+{
+ va_list args;
+ va_start(args, a);
+ vfprintf(stdout, a, args);
+ va_end(args);
+}
+
diff --git a/tools/run_arm_asm.py b/tools/run_arm_asm.py
new file mode 100755
index 0000000..540bc28
--- /dev/null
+++ b/tools/run_arm_asm.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python3
+"""Compile an ARM assembly file and run it with qemu-arm-static."""
+
+from __future__ import annotations
+
+import argparse
+import shutil
+import subprocess
+import sys
+import tempfile
+from pathlib import Path
+
+
+def parse_args() -> argparse.Namespace:
+ if "--" in sys.argv:
+ separator_index = sys.argv.index("--")
+ tool_args = sys.argv[1:separator_index]
+ program_args = sys.argv[separator_index + 1 :]
+ else:
+ tool_args = sys.argv[1:]
+ program_args = []
+
+ parser = argparse.ArgumentParser(
+ description=(
+ "Compile an ARM assembly file with arm-linux-gnueabihf-gcc, "
+ "run it with qemu-arm-static, then print stdout and $?."
+ )
+ )
+ parser.add_argument("asm_file", type=Path, help="path to the assembly file")
+ parser.add_argument(
+ "--cc",
+ default="arm-linux-gnueabihf-gcc",
+ help="cross compiler to use (default: arm-linux-gnueabihf-gcc)",
+ )
+ parser.add_argument(
+ "--qemu",
+ default="qemu-arm-static",
+ help="ARM qemu runner to use (default: qemu-arm-static)",
+ )
+ parser.add_argument(
+ "-o",
+ "--output",
+ type=Path,
+ help="output executable path; defaults to an auto-cleaned temporary file",
+ )
+ parser.add_argument(
+ "-g",
+ "--gdb",
+ action="store_true",
+ help="compile with -g and run qemu in gdb stub mode",
+ )
+ parser.add_argument(
+ "--gdb-port",
+ default="1234",
+ metavar="PORT",
+ help="qemu gdb stub port used with -g/--gdb (default: 1234)",
+ )
+ parser.add_argument(
+ "--gdb-command",
+ help=(
+ "gdb command shown in debug instructions "
+ "(default: first available of arm-linux-gnueabihf-gdb, gdb-multiarch, gdb)"
+ ),
+ )
+ parser.add_argument(
+ "--no-static",
+ action="store_true",
+ help="do not pass -static to the compiler",
+ )
+ parser.add_argument(
+ "--gcc-arg",
+ action="append",
+ default=[],
+ help="extra argument passed to gcc; repeat for multiple args",
+ )
+ parser.add_argument(
+ "--std-c",
+ type=Path,
+ default=Path("tests/std.c"),
+ help="C runtime source compiled with the assembly file (default: tests/std.c)",
+ )
+ parser.add_argument(
+ "--no-std-c",
+ action="store_true",
+ help="do not compile tests/std.c with the assembly file",
+ )
+ args = parser.parse_args(tool_args)
+ args.program_args = program_args
+ return args
+
+
+def require_command(command: str) -> None:
+ if shutil.which(command) is None:
+ print(f"error: command not found: {command}", file=sys.stderr)
+ sys.exit(127)
+
+
+def run_command(command: list[str]) -> subprocess.CompletedProcess[bytes]:
+ try:
+ return subprocess.run(command, capture_output=True, check=False)
+ except OSError as err:
+ print(f"error: failed to run {command[0]}: {err}", file=sys.stderr)
+ sys.exit(127)
+
+
+def choose_gdb_command(requested_command: str | None) -> str:
+ if requested_command:
+ return requested_command
+
+ for command in ("arm-linux-gnueabihf-gdb", "gdb-multiarch", "gdb"):
+ if shutil.which(command) is not None:
+ return command
+
+ return "gdb-multiarch"
+
+
+def main() -> int:
+ args = parse_args()
+ asm_file = args.asm_file
+ if not asm_file.is_file():
+ print(f"error: assembly file not found: {asm_file}", file=sys.stderr)
+ return 2
+
+ std_c = args.std_c
+ if not args.no_std_c and not std_c.is_file():
+ print(f"error: std C file not found: {std_c}", file=sys.stderr)
+ return 2
+
+ require_command(args.cc)
+ require_command(args.qemu)
+
+ with tempfile.TemporaryDirectory(prefix="run-arm-asm-") as temp_dir:
+ output = args.output or Path(temp_dir) / asm_file.with_suffix("").name
+
+ compile_cmd = [args.cc]
+ if not args.no_static:
+ compile_cmd.append("-static")
+ if args.gdb:
+ compile_cmd.append("-g")
+ compile_cmd.extend(args.gcc_arg)
+ compile_cmd.append(str(asm_file))
+ if not args.no_std_c:
+ compile_cmd.append(str(std_c))
+ compile_cmd.extend(["-o", str(output)])
+
+ compile_result = run_command(compile_cmd)
+ sys.stdout.buffer.write(compile_result.stdout)
+ sys.stderr.buffer.write(compile_result.stderr)
+ if compile_result.returncode != 0:
+ return compile_result.returncode
+
+ qemu_cmd = [args.qemu]
+ if args.gdb:
+ qemu_cmd.extend(["-g", args.gdb_port])
+ resolved_output = output.resolve()
+ gdb_command = choose_gdb_command(args.gdb_command)
+ print(f"executable: {resolved_output}", file=sys.stderr, flush=True)
+ print(
+ f"gdb: {gdb_command} {resolved_output} "
+ f"-ex 'target remote :{args.gdb_port}'",
+ file=sys.stderr,
+ flush=True,
+ )
+ print("gdb: use 'si' or 'c'; do not use 'run'", file=sys.stderr, flush=True)
+ qemu_cmd.extend([str(output), *args.program_args])
+
+ run_result = run_command(qemu_cmd)
+ sys.stdout.buffer.write(run_result.stdout)
+ sys.stderr.buffer.write(run_result.stderr)
+ if run_result.stdout and not run_result.stdout.endswith(b"\n"):
+ print()
+ print(f"$? = {run_result.returncode}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())