Volkov: добавить Sprinter Commander
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает результат target-пробы и дерево после UI F6.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p10_rename.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-rename-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p10_rename.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/RENTEST z:/RENRES.TXT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p10_rename_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает результат target-пробы и дерево после UI F8.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p11_delete.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-delete-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p11_delete.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/DELTEST z:/DELRES.TXT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p11_delete_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает обе стороны группового F5 и сравнивает их побайтно.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p12_group_copy.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-copy-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p12_group_copy.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/GRSRC z:/GRDST "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p12_group_copy_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает дерево после группового F8 и проверяет его.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p13_group_delete.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-delete-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p13_group_delete.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/GDEL "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p13_group_delete_fixture.py" verify "$EXTRACTED"
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Проверяет неизменность HDD-fixture после визуального теста цветов.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p14_default_colors.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-default-colors-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p14_default_colors.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/COLORS "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p14_default_colors_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Проверяет носитель после отмены рекурсивного F5.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_cancel.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-cancel-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p15_tree_cancel.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/CSRC z:/CDST "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p15_tree_cancel_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает дерево после рекурсивного F5 и сравнивает его побайтно.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_copy.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-copy-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p15_tree_copy.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/SRC z:/DST "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p15_tree_copy_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Проверяет неизменность source после self-target guard.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_self.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-self-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p15_tree_self.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/SRC "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p15_tree_self_fixture.py" verify "$EXTRACTED"
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает и проверяет результат group F5 с directory roots.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p16_group_tree_copy.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-tree-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p16_group_tree_copy.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/GSRC z:/GDST "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p16_group_tree_copy_fixture.py" verify "$EXTRACTED"
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает и проверяет состояние после runtime-cancel рекурсивного F8.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete_cancel.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete-cancel-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p17_tree_delete_cancel.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/CDEL "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p17_tree_delete_cancel_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает и проверяет результат рекурсивного группового F8.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p17_tree_delete.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/RDEL "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p17_tree_delete_fixture.py" verify "$EXTRACTED"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# Извлекает raw FAT16 и сверяет payload, date/time и атрибуты source/target.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p18_metadata.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-metadata-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p18_metadata.raw"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
python3 "$PROJECT_DIR/tests/p18_metadata_fixture.py" verify "$RAW"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Извлекает результат target-пробы P19 и сверяет все файлы побайтно.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_overwrite.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p19.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir -p "$TEMP_DIR/out"
|
||||
mcopy -s z:/* "$TEMP_DIR/out"
|
||||
python3 "$PROJECT_DIR/tests/p19_overwrite_fixture.py" verify "$TEMP_DIR/out"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Извлекает UI-результат P19 и сверяет policy-последствия побайтно.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_policy.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19-ui-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p19.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir -p "$TEMP_DIR/out"
|
||||
mcopy -s z:/* "$TEMP_DIR/out"
|
||||
python3 "$PROJECT_DIR/tests/p19_policy_fixture.py" verify "$TEMP_DIR/out"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Проверяет изменённый MAME-образ после tests/mame_p4_copy_hdd.lua.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p4_copy.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p4_copy.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/COPYFROM z:/COPYTO "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p4_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает изменённый HDD и проверяет результаты P4CHILD.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p4_exec.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-exec-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p4_exec.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/EXECLEFT z:/EXECRGHT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p4_exec_verify.py" "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает результат низкоуровневой матрицы copy-job из CHD.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_copy_faults.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy-faults-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p5_copy_faults.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/SOURCE z:/TARGET z:/RESULT.TXT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p5_copy_faults_fixture.py" verify "$EXTRACTED"
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Проверяет cleanup почти полного HDD после ошибки копирования.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_enospc.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-enospc-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/enospc.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/SOURCE z:/TARGET "$EXTRACTED/"
|
||||
mcopy z:/FILLER.BIN "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p5_enospc_fixture.py" verify "$EXTRACTED"
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает HDD после P5 и сравнивает все двадцать копий.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_stability.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p5-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p5.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/SOURCE z:/TARGET "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p5_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Проверяет HDD после unload/load и повторного F5.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_media.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-media-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p5_media.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/MEDIA z:/DATA.BIN "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p5_media_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает SORTRES.TXT и проверяет target-регрессию сортировки.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p6_sort.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-sort-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p6_sort.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy z:/SORTRES.TXT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p6_sort_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает SELRES.TXT и проверяет target-регрессию выбора.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p7_select.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-select-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p7_select.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy z:/SELRES.TXT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p7_select_fixture.py" verify "$EXTRACTED"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Извлекает результат target-пробы и дерево после F7.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p9_mkdir.chd}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-mkdir-check.XXXXXX")
|
||||
RAW="$TEMP_DIR/p9_mkdir.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
EXTRACTED="$TEMP_DIR/extracted"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mkdir "$EXTRACTED"
|
||||
mcopy -s z:/MKTEST z:/MKRES.TXT "$EXTRACTED/"
|
||||
python3 "$PROJECT_DIR/tests/p9_mkdir_fixture.py" verify "$EXTRACTED"
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD с target-пробой и UI-fixture F6 local rename.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p10_rename.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
PROBE=${3:-$PROJECT_DIR/build/tests/renrt.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-rename.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p10_rename_fixture.py" create "$TEMP_DIR"
|
||||
sh "$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$PROBE" "$TEMP_DIR/RENTEST"
|
||||
printf 'P10 rename HDD ready: %s\n' "$IMAGE"
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD с target-пробой и UI-fixture F8 delete.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p11_delete.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
PROBE=${3:-$PROJECT_DIR/build/tests/delrt.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-delete.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p11_delete_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$PROBE" "$TEMP_DIR/DELTEST"
|
||||
printf 'P11 delete HDD ready: %s\n' "$IMAGE"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD с двумя панелями для группового F5.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p12_group_copy.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-copy.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p12_group_copy_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/GRSRC" "$TEMP_DIR/GRDST"
|
||||
printf 'P12 group-copy HDD ready: %s\n' "$IMAGE"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture группового F8.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p13_group_delete.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-delete.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p13_group_delete_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/GDEL"
|
||||
printf 'P13 group-delete HDD ready: %s\n' "$IMAGE"
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture стандартных цветов имён.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p14_default_colors.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-default-colors.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p14_default_colors_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/COLORS"
|
||||
printf 'P14 default-colours HDD ready: %s\n' "$IMAGE"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture отмены рекурсивного F5.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_cancel.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-cancel.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p15_tree_cancel_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/CSRC" "$TEMP_DIR/CDST"
|
||||
printf 'P15 tree-cancel HDD ready: %s\n' "$IMAGE"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture рекурсивного F5 одного каталога.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_copy.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-copy.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p15_tree_copy_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/SRC" "$TEMP_DIR/DST"
|
||||
printf 'P15 tree-copy HDD ready: %s\n' "$IMAGE"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture target-inside-source guard.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_self.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-self.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p15_tree_self_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/SRC"
|
||||
printf 'P15 tree-self HDD ready: %s\n' "$IMAGE"
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture group F5 с двумя directory roots.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p16_group_tree_copy.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-tree.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p16_group_tree_copy_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/GSRC" "$TEMP_DIR/GDST"
|
||||
printf 'P16 group-tree HDD ready: %s\n' "$IMAGE"
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture runtime-cancel рекурсивного группового F8.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete_cancel.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete-cancel.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p17_tree_delete_cancel_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/CDEL"
|
||||
printf 'P17 tree-delete cancel HDD ready: %s\n' "$IMAGE"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD-fixture рекурсивного группового F8.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p17_tree_delete_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$TEMP_DIR/RDEL"
|
||||
printf 'P17 tree-delete HDD ready: %s\n' "$IMAGE"
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD с точными FAT date/time и R/H/S/A у source-файла.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p18_metadata.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-metadata.XXXXXX")
|
||||
BASE_CHD="$TEMP_DIR/base.chd"
|
||||
RAW="$TEMP_DIR/p18_metadata.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p18_metadata_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$BASE_CHD" \
|
||||
"$COMMANDER" "$TEMP_DIR/MSRC" "$TEMP_DIR/MDST"
|
||||
|
||||
chdman extractraw -i "$BASE_CHD" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
# Базовый make_hdd копирует дерево без host mtime. Повторная запись с -m
|
||||
# задаёт контролируемые FAT date/time до включения readonly.
|
||||
mcopy -m -o "$TEMP_DIR/MSRC/META.BIN" z:/MSRC/META.BIN
|
||||
mattrib +r +h +s +a z:/MSRC/META.BIN
|
||||
python3 "$PROJECT_DIR/tests/p18_metadata_fixture.py" verify-source "$RAW"
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
chdman createhd -i "$RAW" -o "$IMAGE" -c none \
|
||||
-chs 128,16,32 -ss 512 -f
|
||||
printf 'P18 metadata HDD ready: %s\n' "$IMAGE"
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD для target-пробы транзакционного overwrite.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_overwrite.chd}
|
||||
PROBE=${2:-$PROJECT_DIR/build/tests/p19/delrt.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19.XXXXXX")
|
||||
BASE_CHD="$TEMP_DIR/base.chd"
|
||||
RAW="$TEMP_DIR/p19.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p19_overwrite_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$BASE_CHD" \
|
||||
"$PROBE" "$TEMP_DIR/SOURCE" "$TEMP_DIR/TARGET"
|
||||
chdman extractraw -i "$BASE_CHD" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mattrib +r z:/TARGET/READONLY.BIN
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
chdman createhd -i "$RAW" -o "$IMAGE" -c none \
|
||||
-chs 128,16,32 -ss 512 -f
|
||||
printf 'P19 overwrite HDD ready: %s\n' "$IMAGE"
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD полного UI-сценария conflict-policy P19.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_policy.chd}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19-ui.XXXXXX")
|
||||
BASE_CHD="$TEMP_DIR/base.chd"
|
||||
RAW="$TEMP_DIR/p19.raw"
|
||||
MTC="$TEMP_DIR/mtools.conf"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
python3 "$PROJECT_DIR/tests/p19_policy_fixture.py" create "$TEMP_DIR"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$BASE_CHD" \
|
||||
"$COMMANDER" "$TEMP_DIR/P19SRC" "$TEMP_DIR/P19DST"
|
||||
chdman extractraw -i "$BASE_CHD" -o "$RAW" -f >/dev/null
|
||||
printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC"
|
||||
export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1
|
||||
mattrib +r z:/P19DST/RONLY.BIN
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
chdman createhd -i "$RAW" -o "$IMAGE" -c none \
|
||||
-chs 128,16,32 -ss 512 -f
|
||||
printf 'P19 policy HDD ready: %s\n' "$IMAGE"
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
# Создаёт стандартную FAT12-дискету для P3 stress-теста.
|
||||
# Аргументы: [образ [исполняемый файл]]; по умолчанию — mc.img и Commander.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/../../mame/v306/IMG/mc.img"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p3.XXXXXX")
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir "$TEMP_DIR/MANY" "$TEMP_DIR/BIGDIR"
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 40 ]; do
|
||||
name=$(printf 'M%07d.TXT' "$i")
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$TEMP_DIR/MANY/$name"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 680 ]; do
|
||||
name=$(printf 'B%07d.DAT' "$i")
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$TEMP_DIR/BIGDIR/$name"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
mformat -C -i "$IMAGE" -f 1440 -v SPRCMD ::
|
||||
mcopy -i "$IMAGE" "$EXECUTABLE" ::
|
||||
mmd -i "$IMAGE" ::EMPTY
|
||||
mmd -i "$IMAGE" ::TESTDIR
|
||||
mcopy -i "$IMAGE" "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" ::
|
||||
mcopy -i "$IMAGE" "$PROJECT_DIR/tests/p3_data/ZETA.BIN" ::
|
||||
mcopy -i "$IMAGE" "$PROJECT_DIR/tests/p3_data/INNER.TXT" ::TESTDIR/
|
||||
mcopy -i "$IMAGE" -s "$TEMP_DIR/MANY" ::
|
||||
mcopy -i "$IMAGE" -s "$TEMP_DIR/BIGDIR" ::
|
||||
|
||||
mdir -i "$IMAGE" ::
|
||||
echo "P3 stress floppy ready: $IMAGE (executable: $EXECUTABLE)"
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
# Создаёт отдельный FAT16 HDD для интеграционных тестов Commander.
|
||||
# Аргументы: [образ [исполняемый файл]]; по умолчанию — локальный build/HDD.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p3_stress.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-hdd.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/P3DATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
mkdir "$FIXTURE_ROOT"
|
||||
mkdir "$FIXTURE_ROOT/EMPTY" "$FIXTURE_ROOT/TESTDIR" \
|
||||
"$FIXTURE_ROOT/MANY" "$FIXTURE_ROOT/BIGDIR"
|
||||
|
||||
cp "$PROJECT_DIR/tests/p3_data/INNER.TXT" \
|
||||
"$FIXTURE_ROOT/TESTDIR/INNER.TXT"
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 40 ]; do
|
||||
name=$(printf 'M%07d.TXT' "$i")
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" \
|
||||
"$FIXTURE_ROOT/MANY/$name"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 680 ]; do
|
||||
name=$(printf 'B%07d.DAT' "$i")
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" \
|
||||
"$FIXTURE_ROOT/BIGDIR/$name"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$FIXTURE_ROOT/P3ROOT.TXT"
|
||||
cp "$PROJECT_DIR/tests/p3_data/ZETA.BIN" "$FIXTURE_ROOT/ZETA.BIN"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" \
|
||||
"$FIXTURE_ROOT/EMPTY" "$FIXTURE_ROOT/TESTDIR" \
|
||||
"$FIXTURE_ROOT/MANY" "$FIXTURE_ROOT/BIGDIR" \
|
||||
"$FIXTURE_ROOT/P3ROOT.TXT" "$FIXTURE_ROOT/ZETA.BIN"
|
||||
|
||||
echo "P3 stress HDD ready: $IMAGE (D:, executable: SPRCMD.EXE)"
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Создаёт отдельный FAT16 HDD для интеграционного теста F5.
|
||||
# Аргументы: [образ [исполняемый файл]].
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p4_copy.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/P4DATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p4_fixture.py" create "$FIXTURE_ROOT"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$FIXTURE_ROOT/COPYFROM" "$FIXTURE_ROOT/COPYTO"
|
||||
|
||||
echo "P4 copy HDD ready: $IMAGE (D:, executable: SPRCMD.EXE)"
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
# Создаёт отдельный HDD для EXEC/restore-теста P4.2.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p4_exec.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
CHILD=${3:-$PROJECT_DIR/tests/p4_exec_child/p4child.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-exec.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/P4EXEC"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
mkdir -p "$FIXTURE_ROOT/EXECLEFT" "$FIXTURE_ROOT/EXECRGHT"
|
||||
cp "$CHILD" "$FIXTURE_ROOT/EXECLEFT/P4CHILD.EXE"
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" \
|
||||
"$FIXTURE_ROOT/EXECLEFT/BROKEN.EXE"
|
||||
cp "$PROJECT_DIR/tests/p3_data/INNER.TXT" \
|
||||
"$FIXTURE_ROOT/EXECLEFT/NOTE.TXT"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$FIXTURE_ROOT/EXECLEFT" "$FIXTURE_ROOT/EXECRGHT"
|
||||
|
||||
echo "P4 EXEC HDD ready: $IMAGE (D:, child: EXECLEFT/P4CHILD.EXE)"
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD с отдельной матрицей отказов банкового copy-job.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_copy_faults.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/tests/cperr.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy-faults.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/P5COPY"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p5_copy_faults_fixture.py" create "$FIXTURE_ROOT"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$FIXTURE_ROOT/SOURCE" "$FIXTURE_ROOT/TARGET"
|
||||
|
||||
echo "P5 copy-fault HDD ready: $IMAGE"
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# Создаёт почти полный HDD для реальной ошибки записи ENOSPC.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_enospc.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-enospc.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/ENOSPC"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p5_enospc_fixture.py" create "$FIXTURE_ROOT"
|
||||
python3 "$PROJECT_DIR/tests/p5_enospc_fixture.py" verify-initial "$FIXTURE_ROOT"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$FIXTURE_ROOT/SOURCE" "$FIXTURE_ROOT/TARGET" \
|
||||
"$FIXTURE_ROOT/FILLER.BIN"
|
||||
|
||||
echo "P5 ENOSPC HDD ready: $IMAGE"
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD для P5: 20 копирований, 100 refresh и cleanup-циклы.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_stability.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p5.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/P5DATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p5_fixture.py" create "$FIXTURE_ROOT"
|
||||
python3 "$PROJECT_DIR/tests/p5_fixture.py" verify-initial "$FIXTURE_ROOT"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$FIXTURE_ROOT/SOURCE" "$FIXTURE_ROOT/TARGET"
|
||||
|
||||
echo "P5 stability HDD ready: $IMAGE"
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD для физического unload/load во время F5.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_media.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-media.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/P5MEDIA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p5_media_fixture.py" create "$FIXTURE_ROOT"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$FIXTURE_ROOT/MEDIA"
|
||||
|
||||
echo "P5 media-change HDD ready: $IMAGE"
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
# Создаёт каталоги с итоговыми panel.count ровно 27 и 28 (включая `..`).
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_panel_edges.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-panel-edge.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/EDGES"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")" "$FIXTURE_ROOT/N27" "$FIXTURE_ROOT/N28"
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 26 ]; do
|
||||
name=$(printf 'A%07d.TXT' "$i")
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$FIXTURE_ROOT/N27/$name"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 27 ]; do
|
||||
name=$(printf 'B%07d.TXT' "$i")
|
||||
cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$FIXTURE_ROOT/N28/$name"
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$FIXTURE_ROOT/N27" "$FIXTURE_ROOT/N28"
|
||||
|
||||
echo "P5 panel-edge HDD ready: N27=26+parent, N28=27+parent"
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Создаёт HDD для запуска реального полноэкранного mdview2 из Commander.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_viewer.chd"
|
||||
IMAGE=${1:-$DEFAULT_IMAGE}
|
||||
COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
VIEWER=${3:-$PROJECT_DIR/../../examples/mdview2/mdview2.exe}
|
||||
README=${4:-$PROJECT_DIR/../../examples/mdview2/README.md}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-viewer.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/VIEWDATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")" "$FIXTURE_ROOT/VIEWER"
|
||||
cp "$VIEWER" "$FIXTURE_ROOT/VIEWER/MDVIEW2.EXE"
|
||||
cp "$README" "$FIXTURE_ROOT/VIEWER/README.MD"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$COMMANDER" "$FIXTURE_ROOT/VIEWER"
|
||||
|
||||
echo "P5 viewer HDD ready: VIEWER/MDVIEW2.EXE + README.MD"
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Собирает HDD с target-регрессией и двумя каталогами UI-сортировки.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p6_sort.chd}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
PROBE=${3:-$PROJECT_DIR/build/tests/sortrt.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-sort.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/SORTDATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p6_sort_fixture.py" create "$FIXTURE_ROOT"
|
||||
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$PROBE" "$FIXTURE_ROOT/SORTL" "$FIXTURE_ROOT/SORTR"
|
||||
|
||||
echo "P6 sort HDD ready: $IMAGE"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Собирает HDD target/UI-регрессии выбора записей.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p7_select.chd}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
PROBE=${3:-$PROJECT_DIR/build/tests/selrt.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-select.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/SELDATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p7_select_fixture.py" create "$FIXTURE_ROOT"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$PROBE" "$FIXTURE_ROOT/SELECT"
|
||||
echo "P7 selection HDD ready: $IMAGE"
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Собирает HDD target/UI-регрессии F7 mkdir.
|
||||
|
||||
set -eu
|
||||
|
||||
PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||
IMAGE=${1:-$PROJECT_DIR/build/hdd/p9_mkdir.chd}
|
||||
EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe}
|
||||
PROBE=${3:-$PROJECT_DIR/build/tests/mkrt.exe}
|
||||
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-mkdir.XXXXXX")
|
||||
FIXTURE_ROOT="$TEMP_DIR/MKDATA"
|
||||
|
||||
cleanup()
|
||||
{
|
||||
rm -rf -- "$TEMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
mkdir -p "$(dirname -- "$IMAGE")"
|
||||
python3 "$PROJECT_DIR/tests/p9_mkdir_fixture.py" create "$FIXTURE_ROOT"
|
||||
"$PROJECT_DIR/../../toolchain/make_hdd.sh" "$IMAGE" \
|
||||
"$EXECUTABLE" "$PROBE" "$FIXTURE_ROOT/MKTEST"
|
||||
echo "P9 mkdir HDD ready: $IMAGE"
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Диагностический список MAME image-устройств Sprinter и их свойств.
|
||||
|
||||
local done = false
|
||||
|
||||
emu.register_periodic(function()
|
||||
if done then return end
|
||||
done = true
|
||||
for tag, image in pairs(manager.machine.images) do
|
||||
print(string.format(
|
||||
"[image-probe] tag=%s instance=%s exists=%s readonly=%s file=%s",
|
||||
tag, image.instance_name, tostring(image.exists),
|
||||
tostring(image.readonly), image.filename))
|
||||
end
|
||||
manager.machine:exit()
|
||||
end)
|
||||
@@ -0,0 +1,148 @@
|
||||
-- 0.2/P10: F6 local rename, unchanged name, collision и group guard.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, b={"P1.5",0x10}, c={"P1.4",0x08},
|
||||
d={"P1.4",0x04}, e={"P1.4",0x20}, f={"P1.5",0x02},
|
||||
i={"P1.4",0x01}, l={"P2.2",0x08}, m={"P2.0",0x10},
|
||||
n={"P2.0",0x08}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, t={"P1.5",0x20}, x={"P1.2",0x10},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
local function backspaces(t, count)
|
||||
for _ = 1, count do
|
||||
key(t, "P2.1", 0x20)
|
||||
t = t + 0.18
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "RENRT.EXE\n")
|
||||
type_text(12.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(18.0, "P2.1", 0x10) -- enter RENTEST.
|
||||
key(20.0, "P2.3", 0x10) -- Down x4 -> UIOLD.TXT.
|
||||
key(20.5, "P2.3", 0x10)
|
||||
key(21.0, "P2.3", 0x10)
|
||||
key(21.5, "P2.3", 0x10)
|
||||
key(23.0, "P2.1", 0x80) -- F6.
|
||||
local t = backspaces(24.0, 9)
|
||||
type_text(t, "final.bin")
|
||||
key(t + 1.4, "P2.1", 0x10)
|
||||
|
||||
key(32.0, "P2.1", 0x80) -- F6, имя без изменений.
|
||||
key(33.0, "P2.1", 0x10)
|
||||
|
||||
key(36.0, "P2.1", 0x80) -- F6, collision с EXIST.TXT.
|
||||
t = backspaces(37.0, 9)
|
||||
type_text(t, "exist.txt")
|
||||
key(t + 1.4, "P2.1", 0x10)
|
||||
|
||||
key(44.0, "P2.5", 0x01) -- Home -> `..`.
|
||||
key(45.0, "P2.1", 0x80) -- F6 guard virtual.
|
||||
key(48.0, "P2.3", 0x10) -- NEWDIR.
|
||||
key(49.0, "P2.6", 0x01) -- Insert dir; cursor -> file.
|
||||
key(50.0, "P2.1", 0x80) -- group ambiguity guard.
|
||||
|
||||
key(54.0, "P2.3", 0x80) -- F10.
|
||||
key(56.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
19.0, -- target probe already renamed file and directory;
|
||||
23.5, -- dialog default UIOLD.TXT;
|
||||
30.5, -- FINAL.BIN created and focused;
|
||||
34.0, -- unchanged name skipped;
|
||||
42.0, -- existing target rejected;
|
||||
46.0, -- virtual `..` rejected;
|
||||
51.0, -- selected dir elsewhere makes group action explicit;
|
||||
58.0 -- clean DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p10-rename] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 60.0 then
|
||||
print("[p10-rename] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,140 @@
|
||||
-- 0.2/P11: F8 cancel, file, empty/nonempty directory и guards.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
l={"P2.2",0x08}, m={"P2.0",0x10}, n={"P2.0",0x08},
|
||||
p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08},
|
||||
t={"P1.5",0x20}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "DELRT.EXE\n")
|
||||
type_text(12.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(18.0, "P2.1", 0x10) -- enter DELTEST.
|
||||
key(20.0, "P2.3", 0x10) -- Down x4 -> REMOVE.TXT.
|
||||
key(20.5, "P2.3", 0x10)
|
||||
key(21.0, "P2.3", 0x10)
|
||||
key(21.5, "P2.3", 0x10)
|
||||
key(23.0, "P2.2", 0x80) -- F8 file, then cancel.
|
||||
key(25.0, "P2.0", 0x08) -- N.
|
||||
|
||||
key(28.0, "P2.2", 0x80) -- F8 file, confirm.
|
||||
key(30.0, "P2.1", 0x10)
|
||||
|
||||
key(34.0, "P2.3", 0x10) -- EMPTYDIR.
|
||||
key(35.0, "P2.2", 0x80)
|
||||
key(37.0, "P2.1", 0x10)
|
||||
|
||||
key(40.0, "P2.3", 0x10) -- NONEMPTY.
|
||||
key(41.0, "P2.2", 0x80)
|
||||
key(43.0, "P2.1", 0x10)
|
||||
|
||||
key(46.0, "P2.5", 0x01) -- Home -> `..`.
|
||||
key(47.0, "P2.2", 0x80) -- virtual guard.
|
||||
key(50.0, "P2.3", 0x10) -- NONEMPTY.
|
||||
key(51.0, "P2.6", 0x01) -- select dir, cursor -> KEEP.
|
||||
key(52.0, "P2.2", 0x80) -- group ambiguity guard.
|
||||
|
||||
key(56.0, "P2.3", 0x80) -- F10.
|
||||
key(58.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
19.0, -- target probe left UI fixture intact;
|
||||
23.5, -- file confirmation;
|
||||
26.0, -- cancel preserved REMOVE.TXT;
|
||||
32.0, -- file deleted;
|
||||
39.0, -- empty directory deleted;
|
||||
45.0, -- nonempty directory rejected;
|
||||
48.0, -- virtual `..` rejected;
|
||||
53.0, -- ambiguous group rejected;
|
||||
60.0 -- clean DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p11-delete] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 62.0 then
|
||||
print("[p11-delete] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,125 @@
|
||||
-- Регрессия P12/P16: выбранные файлы и каталог копируются одним F5.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(16.0, "P1.6", 0x20) -- Tab -> right, cursor GRDST.
|
||||
key(17.0, "P2.1", 0x10) -- right enters GRDST.
|
||||
key(18.0, "P1.6", 0x20) -- Tab -> left.
|
||||
key(19.0, "P2.3", 0x10) -- left GRDST -> GRSRC.
|
||||
key(20.0, "P2.1", 0x10) -- enter GRSRC.
|
||||
|
||||
key(23.0, "P2.3", 0x10) -- ADIR.
|
||||
key(24.0, "P2.6", 0x01) -- select ADIR, cursor A.BIN.
|
||||
key(25.0, "P2.6", 0x01) -- A.BIN.
|
||||
key(26.0, "P2.6", 0x01) -- B.BIN.
|
||||
key(27.0, "P2.6", 0x01) -- C.BIN.
|
||||
key(30.0, "P2.1", 0x40) -- F5 group success.
|
||||
key(36.0, "P2.1", 0x40) -- second F5 stops at existing A.BIN.
|
||||
key(41.0, "P1.6", 0x20) -- show refreshed passive panel.
|
||||
key(46.0, "P2.3", 0x80) -- F10.
|
||||
key(48.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
21.5, -- source/target panels opened;
|
||||
28.5, -- ADIR + three files selected;
|
||||
34.0, -- top-level files and selected directory copied;
|
||||
39.0, -- repeat stopped at existing target;
|
||||
43.0, -- passive panel shows three files;
|
||||
50.0 -- clean DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p12-group-copy] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 52.0 then
|
||||
print("[p12-group-copy] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,121 @@
|
||||
-- 0.2/P13 regression: file-only group F8 leaves unselected directories.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(17.0, "P2.1", 0x10) -- enter GDEL.
|
||||
key(20.0, "P2.3", 0x10) -- ADIR.
|
||||
key(21.0, "P2.3", 0x10) -- EMPTYDIR.
|
||||
key(22.0, "P2.3", 0x10) -- A.TXT.
|
||||
key(23.0, "P2.6", 0x01) -- select A.TXT -> B.BIN.
|
||||
key(24.0, "P2.6", 0x01) -- select B.BIN -> KEEP.TXT.
|
||||
key(27.0, "P2.2", 0x80) -- F8 group, cancel.
|
||||
key(29.0, "P2.0", 0x08) -- N.
|
||||
key(32.0, "P2.2", 0x80) -- F8 group, confirm.
|
||||
key(34.0, "P2.1", 0x10)
|
||||
|
||||
key(42.0, "P2.3", 0x80) -- F10.
|
||||
key(44.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
19.0, -- исходное дерево;
|
||||
25.0, -- два каталога и два файла выбраны;
|
||||
27.5, -- group confirmation;
|
||||
30.0, -- cancel сохранил всё;
|
||||
36.0, -- файлы удалены, невыбранные каталоги сохранены;
|
||||
46.0 -- clean DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p13-group-delete] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 48.0 then
|
||||
print("[p13-group-delete] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,106 @@
|
||||
-- 1.0/P14: стандартные цвета каталогов, EXE и остальных файлов.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
key(17.0, "P2.1", 0x10) -- enter COLORS.
|
||||
key(24.0, "P2.3", 0x80) -- F10.
|
||||
key(26.0, "P2.1", 0x10) -- confirm exit.
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
19.0, -- известный набор: dir, EXE, ZIP, no-ext, TXT;
|
||||
29.0 -- clean DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p14-default-colors] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 31.0 then
|
||||
print("[p14-default-colors] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,105 @@
|
||||
-- 0.2/P15 hardening: Esc посреди 4-МБ файла tree-job.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
key(16.0, "P1.6", 0x20) -- right.
|
||||
key(17.0, "P2.1", 0x10) -- enter CDST.
|
||||
key(18.0, "P1.6", 0x20) -- left.
|
||||
key(19.0, "P2.3", 0x10) -- CDST -> CSRC.
|
||||
key(20.0, "P2.1", 0x10) -- enter CSRC.
|
||||
key(21.0, "P2.3", 0x10) -- TREE.
|
||||
key(23.0, "P2.1", 0x40) -- F5.
|
||||
key(24.5, "P1.6", 0x40) -- Esc during BIG.BIN.
|
||||
key(31.0, "P2.3", 0x80) -- F10.
|
||||
key(33.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {22.0, 27.0, 36.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p15-tree-cancel] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
if elapsed >= 38.0 then
|
||||
print("[p15-tree-cancel] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,119 @@
|
||||
-- 0.2/P15: рекурсивный F5 текущего каталога, depth=16 и EEXIST repeat.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(16.0, "P1.6", 0x20) -- right panel.
|
||||
key(17.0, "P2.1", 0x10) -- enter DST.
|
||||
key(18.0, "P1.6", 0x20) -- left panel.
|
||||
key(19.0, "P2.3", 0x10) -- DST -> SRC.
|
||||
key(20.0, "P2.1", 0x10) -- enter SRC.
|
||||
key(21.0, "P2.3", 0x10) -- `..` -> TREE.
|
||||
|
||||
key(23.0, "P2.1", 0x40) -- F5 recursive success.
|
||||
key(34.0, "P2.1", 0x40) -- repeat: target TREE exists.
|
||||
key(39.0, "P1.6", 0x20) -- show refreshed passive panel.
|
||||
key(44.0, "P2.3", 0x80) -- F10.
|
||||
key(46.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
22.0, -- source TREE and empty destination;
|
||||
31.0, -- recursive success;
|
||||
37.0, -- repeat stopped on EEXIST;
|
||||
41.0, -- passive panel contains TREE;
|
||||
49.0 -- clean DSS prompt, including dynamic EMM cleanup.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p15-tree-copy] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 51.0 then
|
||||
print("[p15-tree-copy] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,109 @@
|
||||
-- 0.2/P15 hardening: passive target лежит внутри копируемого source root.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(16.0, "P1.6", 0x20) -- right.
|
||||
key(17.0, "P2.1", 0x10) -- enter SRC.
|
||||
key(18.0, "P2.3", 0x10) -- TREE.
|
||||
key(19.0, "P2.1", 0x10) -- enter TREE.
|
||||
key(20.0, "P2.3", 0x10) -- INNER.
|
||||
key(21.0, "P2.1", 0x10) -- enter INNER: passive target.
|
||||
key(22.0, "P1.6", 0x20) -- left root.
|
||||
key(23.0, "P2.1", 0x10) -- enter SRC.
|
||||
key(24.0, "P2.3", 0x10) -- TREE: source root.
|
||||
key(26.0, "P2.1", 0x40) -- F5 must refuse before mkdir.
|
||||
key(32.0, "P2.3", 0x80) -- F10.
|
||||
key(34.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {25.0, 29.0, 37.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p15-tree-self] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
if elapsed >= 39.0 then
|
||||
print("[p15-tree-self] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,109 @@
|
||||
-- 0.2/P16: выбранный файл и два выбранных каталога копируются одним F5.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
key(16.0, "P1.6", 0x20) -- right.
|
||||
key(17.0, "P2.1", 0x10) -- enter GDST.
|
||||
key(18.0, "P1.6", 0x20) -- left.
|
||||
key(19.0, "P2.3", 0x10) -- GDST -> GSRC.
|
||||
key(20.0, "P2.1", 0x10) -- enter GSRC.
|
||||
key(21.0, "P2.3", 0x10) -- ADIR.
|
||||
key(22.0, "P2.6", 0x01) -- select ADIR -> BDIR.
|
||||
key(23.0, "P2.6", 0x01) -- select BDIR -> KEEP.TXT.
|
||||
key(24.0, "P2.3", 0x10) -- KEEP.TXT -> ROOT.BIN.
|
||||
key(25.0, "P2.6", 0x01) -- select ROOT.BIN.
|
||||
key(28.0, "P2.1", 0x40) -- group F5.
|
||||
key(39.0, "P1.6", 0x20) -- passive panel.
|
||||
key(44.0, "P2.3", 0x80) -- F10.
|
||||
key(46.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {26.0, 36.0, 41.0, 49.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p16-group-tree] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
if elapsed >= 51.0 then
|
||||
print("[p16-group-tree] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,121 @@
|
||||
-- 0.2/P17: group F8 удаляет выбранные файлы и каталоги рекурсивно.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(17.0, "P2.1", 0x10) -- enter RDEL.
|
||||
key(20.0, "P2.3", 0x10) -- `..` -> ADIR.
|
||||
key(21.0, "P2.6", 0x01) -- select ADIR -> BDIR.
|
||||
key(22.0, "P2.6", 0x01) -- select BDIR -> KEEPDIR.
|
||||
key(23.0, "P2.3", 0x10) -- KEEPDIR -> KEEP.TXT.
|
||||
key(24.0, "P2.3", 0x10) -- KEEP.TXT -> TOP.BIN.
|
||||
key(25.0, "P2.6", 0x01) -- select TOP.BIN.
|
||||
|
||||
key(27.0, "P2.2", 0x80) -- first group F8: cancel confirmation.
|
||||
key(29.0, "P2.0", 0x08) -- N.
|
||||
key(32.0, "P2.2", 0x80) -- second group F8.
|
||||
key(34.0, "P2.1", 0x10) -- confirm.
|
||||
|
||||
key(42.0, "P2.3", 0x80) -- F10.
|
||||
key(44.0, "P2.1", 0x10) -- confirm exit.
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {
|
||||
19.0, -- исходное дерево;
|
||||
25.5, -- два каталога и файл выбраны;
|
||||
27.5, -- подтверждение группы;
|
||||
30.0, -- cancel до mutation сохранил всё;
|
||||
39.0, -- выбранные деревья удалены;
|
||||
46.0 -- clean DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p17-tree-delete] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 48.0 then
|
||||
print("[p17-tree-delete] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,110 @@
|
||||
-- 0.2/P17: Esc во время рекурсивного group F8.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(17.0, "P2.1", 0x10) -- enter CDEL.
|
||||
key(20.0, "P2.3", 0x10) -- `..` -> TREE.
|
||||
key(21.0, "P2.6", 0x01) -- select TREE -> KEEP.TXT.
|
||||
key(22.0, "P2.3", 0x10) -- KEEP.TXT -> TOP.BIN.
|
||||
key(23.0, "P2.6", 0x01) -- select TOP.BIN.
|
||||
key(25.0, "P2.2", 0x80) -- group F8.
|
||||
key(27.0, "P2.1", 0x10) -- confirm; TOP.BIN удаляется первым.
|
||||
key(27.5, "P1.6", 0x40) -- Esc во время enqueue большого дерева.
|
||||
|
||||
key(35.0, "P2.3", 0x80) -- F10 после cleanup job.
|
||||
key(37.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {24.0, 25.5, 31.0, 39.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p17-tree-delete-cancel] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 41.0 then
|
||||
print("[p17-tree-delete-cancel] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,109 @@
|
||||
-- 0.2/P18: F5 сохраняет точные FAT date/time и R/H/S/A.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(16.0, "P1.6", 0x20) -- right panel.
|
||||
key(17.0, "P2.1", 0x10) -- enter MDST.
|
||||
key(18.0, "P1.6", 0x20) -- left panel.
|
||||
key(19.0, "P2.3", 0x10) -- MDST -> MSRC.
|
||||
key(20.0, "P2.1", 0x10) -- enter MSRC.
|
||||
key(22.0, "P2.3", 0x10) -- `..` -> META.BIN.
|
||||
key(25.0, "P2.1", 0x40) -- F5.
|
||||
key(31.0, "P1.6", 0x20) -- inspect refreshed MDST.
|
||||
key(36.0, "P2.3", 0x80) -- F10.
|
||||
key(38.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
|
||||
local snap_times = {23.0, 29.0, 33.0, 40.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p18-metadata] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 42.0 then
|
||||
print("[p18-metadata] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,87 @@
|
||||
-- P19: запускает target-пробу overwrite и фиксирует итоговую таблицу.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
d={"P1.4",0x04}, e={"P1.4",0x20}, l={"P2.2",0x08},
|
||||
r={"P1.5",0x01}, t={"P1.5",0x20}, x={"P1.2",0x10},
|
||||
["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else key(t, def[1], def[2]) end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "DELRT.EXE\n")
|
||||
key(18.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
local snap_times = {15.0, 20.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
end
|
||||
end
|
||||
if elapsed >= 22.0 then manager.machine:exit() end
|
||||
end)
|
||||
@@ -0,0 +1,131 @@
|
||||
-- P19 UI: overwrite-all, readonly re-prompt, skip-all, rename и cancel.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, b={"P1.5",0x10}, c={"P1.4",0x08},
|
||||
d={"P1.4",0x04}, e={"P1.4",0x20}, i={"P1.4",0x01},
|
||||
m={"P2.0",0x10}, n={"P2.0",0x08}, o={"P2.2",0x02},
|
||||
p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08},
|
||||
t={"P1.5",0x20}, x={"P1.2",0x10}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else key(t, def[1], def[2]) end
|
||||
t = t + 0.14
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(10.0, "SPRCMD.EXE\n")
|
||||
key(16.0, "P1.6", 0x20) -- right root.
|
||||
key(17.0, "P2.1", 0x10) -- enter P19DST.
|
||||
key(18.0, "P1.6", 0x20) -- left root.
|
||||
key(19.0, "P2.3", 0x10) -- P19DST -> P19SRC.
|
||||
key(20.0, "P2.1", 0x10) -- enter P19SRC.
|
||||
|
||||
key(22.0, "P2.3", 0x10) -- AONE.
|
||||
key(23.0, "P2.6", 0x01) -- select AONE -> ATWO.
|
||||
key(24.0, "P2.6", 0x01) -- select ATWO -> CANCEL.
|
||||
key(25.0, "P2.3", 0x10) -- REN.
|
||||
key(26.0, "P2.3", 0x10) -- RONLY.
|
||||
key(27.0, "P2.6", 0x01) -- select RONLY -> SONE.
|
||||
key(30.0, "P2.1", 0x40) -- group F5.
|
||||
key(33.0, "P1.6", 0x04) -- A = overwrite all.
|
||||
key(40.0, "P2.2", 0x02) -- O = explicit readonly overwrite.
|
||||
|
||||
chord(47.0, "P1.1", 0x02, "P1.5", 0x01) -- refresh/reset selection.
|
||||
key(49.0, "P2.6", 0x01) -- select SONE -> STWO.
|
||||
key(50.0, "P2.6", 0x01) -- select STWO.
|
||||
key(52.0, "P2.1", 0x40) -- group F5.
|
||||
key(56.0, "P2.0", 0x08) -- N = skip all.
|
||||
|
||||
chord(60.0, "P1.1", 0x02, "P1.5", 0x01)
|
||||
key(62.0, "P2.5", 0x01) -- Home -> `..`.
|
||||
key(63.0, "P2.3", 0x10) -- AONE.
|
||||
key(64.0, "P2.3", 0x10) -- ATWO.
|
||||
key(65.0, "P2.3", 0x10) -- CANCEL.
|
||||
key(66.0, "P2.3", 0x10) -- REN.
|
||||
key(68.0, "P2.1", 0x40) -- single F5.
|
||||
key(72.0, "P1.5", 0x01) -- R = rename.
|
||||
type_text(74.0, "NEXT.BIN\n")
|
||||
|
||||
key(80.0, "P2.5", 0x01) -- Home -> `..`.
|
||||
key(80.5, "P2.3", 0x10) -- AONE.
|
||||
key(81.0, "P2.3", 0x10) -- ATWO.
|
||||
key(81.5, "P2.3", 0x10) -- CANCEL.
|
||||
key(83.0, "P2.1", 0x40)
|
||||
key(87.0, "P1.6", 0x40) -- Esc conflict.
|
||||
key(90.0, "P1.6", 0x20) -- inspect target.
|
||||
key(95.0, "P2.3", 0x80) -- F10.
|
||||
key(97.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(left, right) return left[1] < right[1] end)
|
||||
local snap_times = {31.5, 38.0, 44.0, 54.0, 58.0, 70.0, 76.0, 85.0, 89.0, 92.0, 99.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local value = manager.machine.time
|
||||
return value.seconds + value.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p19-policy] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
if elapsed >= 101.0 then manager.machine:exit() end
|
||||
end)
|
||||
@@ -0,0 +1,130 @@
|
||||
-- Автоматический MAME-сценарий для двухпанельного skeleton P2.
|
||||
-- Использует реальные поля AT-клавиатуры, включая PgDn и F1/F10, которых
|
||||
-- нет в текстовом алфавите mame_interactive.py.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, c={"P1.4",0x08}, d={"P1.4",0x04},
|
||||
e={"P1.4",0x20}, m={"P2.0",0x10}, n={"P2.0",0x08},
|
||||
p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08},
|
||||
x={"P1.2",0x10}, y={"P2.0",0x01},
|
||||
[" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "a:\\SPRCMD .EXE\n")
|
||||
|
||||
key(17.0, "P1.6", 0x20) -- Tab: активировать правую панель.
|
||||
key(21.0, "P1.2", 0x20) -- PgDn: cursor 27, top 1, системный scroll.
|
||||
key(22.0, "P2.3", 0x10) -- Down: cursor 28, top 2, ещё один scroll.
|
||||
key(25.0, "P2.3", 0x80) -- F10: диалог выхода.
|
||||
key(29.0, "P2.0", 0x08) -- N: отменить выход и восстановить экран.
|
||||
key(33.0, "P1.2", 0x40) -- F1: единый диалог заглушки.
|
||||
key(37.0, "P1.6", 0x40) -- Esc: закрыть сообщение.
|
||||
key(40.0, "P2.3", 0x80) -- F10 повторно.
|
||||
key(44.0, "P2.1", 0x10) -- Enter: подтвердить штатный выход.
|
||||
|
||||
-- Немедленный второй запуск ловит утечку EMM между процессами. Сам EXE
|
||||
-- дополнительно сравнивает free pages до alloc и после cleanup.
|
||||
type_text(50.0, "a:\\SPRCMD .EXE\n")
|
||||
key(59.0, "P2.3", 0x80) -- F10 во втором процессе.
|
||||
key(63.0, "P2.1", 0x10) -- Подтвердить второй cleanup.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
15.0, 19.0, 23.0, 27.0, 31.0, 35.0, 39.0, 42.0, 47.0,
|
||||
57.0, 61.0, 66.0
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p2-ui] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 68.0 then
|
||||
print("[p2-ui] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,104 @@
|
||||
-- Изолированная MAME-проба предельного каталога DSS.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
i={"P1.4",0x01}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
x={"P1.2",0x10}, ["3"]={"P1.4",0x40},
|
||||
[" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "a:\\P3DIR .EXE\n")
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local snapped = false
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
if not snapped and elapsed >= 18.0 then
|
||||
snapped = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p3-dir-probe] snapshot t=" .. elapsed)
|
||||
end
|
||||
if elapsed >= 20.0 then
|
||||
print("[p3-dir-probe] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,98 @@
|
||||
-- Запуск P3MAP.EXE и снимок матрицы влияния W3 на F_NEXT.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, e={"P1.4",0x20}, m={"P2.0",0x10},
|
||||
p={"P2.3",0x20}, x={"P1.2",0x10}, ["3"]={"P1.4",0x40},
|
||||
[" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "a:\\P3MAP .EXE\n")
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local snapped = false
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function() start_time = now() end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
if not snapped and elapsed >= 30.0 then
|
||||
snapped = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p3-enum-matrix] snapshot t=" .. elapsed)
|
||||
end
|
||||
if elapsed >= 32.0 then
|
||||
print("[p3-enum-matrix] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,137 @@
|
||||
-- Автоматический MAME-сценарий P3: настоящие каталоги и две панели.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, c={"P1.4",0x08}, d={"P1.4",0x04},
|
||||
e={"P1.4",0x20}, m={"P2.0",0x10}, p={"P2.3",0x20},
|
||||
r={"P1.5",0x01}, s={"P1.2",0x08}, x={"P1.2",0x10},
|
||||
[" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "a:\\SPRCMD .EXE\n")
|
||||
|
||||
key(17.0, "P2.1", 0x10) -- Enter: левая -> TESTDIR.
|
||||
key(22.0, "P2.1", 0x10) -- Enter на `..`: обратно в корень.
|
||||
key(27.0, "P2.1", 0x10) -- Снова открыть TESTDIR.
|
||||
key(29.0, "P2.3", 0x10) -- Выбрать INNER.TXT.
|
||||
chord(31.0, "P1.1", 0x02, "P1.5", 0x01) -- Ctrl+R: сохранить выбор имени.
|
||||
key(36.0, "P1.6", 0x20) -- Tab: правая панель.
|
||||
key(38.0, "P2.3", 0x10) -- Выбрать P3ROOT.TXT справа.
|
||||
key(43.0, "P1.6", 0x20) -- Tab: вернуться в левую.
|
||||
chord(45.0, "P1.1", 0x02, "P1.2", 0x01) -- Ctrl+PgUp: левая -> корень.
|
||||
key(50.0, "P2.3", 0x80) -- F10.
|
||||
key(55.0, "P2.1", 0x10) -- Enter: cleanup.
|
||||
|
||||
-- Второй процесс проверяет освобождение EMM и повторную инициализацию темы.
|
||||
type_text(61.0, "a:\\SPRCMD .EXE\n")
|
||||
key(70.0, "P2.3", 0x80)
|
||||
key(74.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
15.0, -- root/root: TESTDIR должен быть первым;
|
||||
20.0, -- left TESTDIR: `..`, INNER.TXT; right root;
|
||||
25.0, -- Enter на `..` вернул left в root;
|
||||
34.0, -- refresh сохранил INNER.TXT и cursor=1;
|
||||
41.0, -- right root cursor=1, left TESTDIR cursor=1;
|
||||
48.0, -- Ctrl+PgUp изменил только left;
|
||||
53.0, -- quit dialog;
|
||||
58.0, -- DSS после первого cleanup;
|
||||
68.0, -- второй root/root;
|
||||
72.0, -- второй quit dialog;
|
||||
77.0 -- DSS после второго cleanup.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p3-panels] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 79.0 then
|
||||
print("[p3-panels] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,107 @@
|
||||
-- Полная MAME-проба scan/store/sort большого каталога P3.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
a={"P1.6",0x04}, c={"P1.4",0x08}, d={"P1.4",0x04},
|
||||
e={"P1.4",0x20}, n={"P2.0",0x08}, p={"P2.3",0x20},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10},
|
||||
["3"]={"P1.4",0x40}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\\"]={"P2.1",0x04},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "a:\\P3SCAN .EXE\n")
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {20.0, 45.0, 75.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p3-scan-probe] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
if elapsed >= 78.0 then
|
||||
print("[p3-scan-probe] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,137 @@
|
||||
-- P3 stress: пустой каталог, 40 строк и явный маркер предела DSS.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04},
|
||||
e={"P1.4",0x20}, m={"P2.0",0x10}, p={"P2.3",0x20},
|
||||
r={"P1.5",0x01}, s={"P1.2",0x08}, x={"P1.2",0x10},
|
||||
[" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
-- Root order: BIGDIR, EMPTY, MANY, TESTDIR, P3ROOT, SPRCMD, ZETA.
|
||||
key(17.0, "P2.3", 0x10) -- EMPTY.
|
||||
key(18.0, "P2.1", 0x10) -- Enter EMPTY.
|
||||
key(24.0, "P2.1", 0x10) -- Enter `..`.
|
||||
key(27.0, "P2.3", 0x10) -- EMPTY.
|
||||
key(28.0, "P2.3", 0x10) -- MANY.
|
||||
key(29.0, "P2.1", 0x10) -- Enter MANY.
|
||||
key(36.0, "P1.2", 0x20) -- PgDn -> cursor 27/top 1.
|
||||
key(38.0, "P2.3", 0x10) -- Down -> cursor 28/top 2.
|
||||
chord(43.0, "P1.1", 0x02, "P1.2", 0x01) -- Ctrl+PgUp -> root.
|
||||
key(46.0, "P2.1", 0x10) -- Enter BIGDIR (index 0).
|
||||
|
||||
-- BIGDIR scan может быть долгим; последующие события намеренно отнесены.
|
||||
key(76.0, "P2.5", 0x20) -- End: красный `>>> MORE...`.
|
||||
key(81.0, "P2.1", 0x10) -- Enter: пояснение, без перехода.
|
||||
key(86.0, "P2.3", 0x80) -- F10.
|
||||
key(90.0, "P2.1", 0x10) -- cleanup.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
15.0, -- root count=7, dirs before files;
|
||||
21.0, -- EMPTY: only synthetic `..`, count=1;
|
||||
32.0, -- MANY: count=41;
|
||||
40.0, -- real list cursor=28/top=2;
|
||||
73.0, -- BIGDIR: частичный список и признак усечения DSS;
|
||||
79.0, -- End: выбран красный виртуальный `>>> MORE...`;
|
||||
84.0, -- Enter: статус о недоступном остатке, каталог не меняется;
|
||||
88.0, -- quit dialog over intact screen;
|
||||
93.0 -- DSS after cleanup.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p3-stress] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 95.0 then
|
||||
print("[p3-stress] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,142 @@
|
||||
-- P4/F5 на HDD D: успех >64 КБ, отказ overwrite и отмена с cleanup.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, m={"P2.0",0x10},
|
||||
p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08},
|
||||
x={"P1.2",0x10}, [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- Enter: левая -> COPYFROM.
|
||||
key(17.0, "P1.6", 0x20) -- Tab: правая панель.
|
||||
key(18.0, "P2.3", 0x10) -- Down: COPYTO.
|
||||
key(19.0, "P2.1", 0x10) -- Enter: правая -> COPYTO.
|
||||
key(21.0, "P1.6", 0x20) -- Tab: левая панель.
|
||||
|
||||
-- `..`, CANCEL, EMPTY, EXIST, F65535, F65536, K4095, K4096, LARGE.
|
||||
for i = 1, 8 do key(22.0 + (i - 1) * 0.20, "P2.3", 0x10) end
|
||||
key(25.0, "P2.1", 0x40) -- F5: LARGE.BIN (131109 байт).
|
||||
|
||||
key(31.0, "P2.5", 0x01) -- Home.
|
||||
for i = 1, 3 do key(32.0 + (i - 1) * 0.20, "P2.3", 0x10) end
|
||||
key(33.5, "P2.1", 0x40) -- F5: EXIST.TXT, overwrite запрещён.
|
||||
|
||||
key(36.0, "P2.5", 0x01) -- Home.
|
||||
key(37.0, "P2.3", 0x10) -- CANCEL.BIN.
|
||||
key(38.0, "P2.1", 0x40) -- F5: начать 4-МБ копирование.
|
||||
key(41.0, "P1.6", 0x40) -- Esc: отменить между блоками.
|
||||
|
||||
key(44.0, "P2.3", 0x10) -- Down: EMPTY.ZRO.
|
||||
key(44.5, "P2.1", 0x40) -- F5: нулевой файл.
|
||||
|
||||
key(48.0, "P2.3", 0x80) -- F10.
|
||||
key(50.0, "P2.1", 0x10) -- Enter: cleanup и возврат в DSS.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
14.0, -- обе панели в корне D:;
|
||||
20.5, -- COPYFROM/COPYTO разведены по панелям;
|
||||
25.8, 28.5, 30.0, -- начало, поздний прогресс и результат LARGE.BIN;
|
||||
35.0, -- отказ перезаписи EXIST.TXT;
|
||||
39.5, -- активный прогресс CANCEL.BIN;
|
||||
43.0, -- отмена и cleanup temp;
|
||||
46.0, -- нулевой EMPTY.ZRO скопирован;
|
||||
49.0, -- диалог выхода;
|
||||
52.0 -- возврат в D:\>.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p4-copy-hdd] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 54.0 then
|
||||
print("[p4-copy-hdd] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,141 @@
|
||||
-- P4.2 на HDD D: child меняет mode/palette/CWD и обе панели.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- левая -> EXECLEFT.
|
||||
key(17.0, "P1.6", 0x20) -- Tab.
|
||||
key(18.0, "P2.3", 0x10) -- EXECRGHT.
|
||||
key(19.0, "P2.1", 0x10) -- правая -> EXECRGHT.
|
||||
key(21.0, "P1.6", 0x20) -- Tab влево.
|
||||
|
||||
-- `..`, BROKEN.EXE, NOTE.TXT, P4CHILD.EXE.
|
||||
for i = 1, 3 do key(22.0 + (i - 1) * 0.20, "P2.3", 0x10) end
|
||||
key(23.5, "P2.1", 0x10) -- Enter: P4CHILD.EXE.
|
||||
key(28.0, "P2.1", 0x10) -- Enter внутри child: return 5A.
|
||||
|
||||
-- После refresh: `..`, BROKEN, CREATED, NOTE, P4CHILD.
|
||||
key(34.0, "P2.5", 0x01) -- Home.
|
||||
key(35.0, "P2.3", 0x10) -- BROKEN.EXE.
|
||||
key(36.0, "P2.1", 0x10) -- ошибка загрузчика EXE.
|
||||
|
||||
key(40.0, "P2.5", 0x01) -- Home.
|
||||
for i = 1, 3 do key(41.0 + (i - 1) * 0.20, "P2.3", 0x10) end
|
||||
key(42.5, "P2.1", 0x10) -- NOTE.TXT: тип не поддерживается.
|
||||
|
||||
key(47.0, "P2.3", 0x80) -- F10.
|
||||
key(49.0, "P2.1", 0x10) -- cleanup.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
14.0, -- root D:;
|
||||
20.5, -- панели EXECLEFT/EXECRIGHT;
|
||||
26.0, -- экран дочерней программы 40x32;
|
||||
32.0, -- Commander восстановлен, обе панели refreshed;
|
||||
38.0, -- ошибка BROKEN.EXE без завершения Commander;
|
||||
45.0, -- NOTE.TXT не запускается;
|
||||
48.0, -- quit dialog поверх восстановленного экрана;
|
||||
51.0 -- чистый возврат в D:\>.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p4-exec-hdd] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 53.0 then
|
||||
print("[p4-exec-hdd] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,107 @@
|
||||
-- P5: отдельная детерминированная матрица состояний copy-job на HDD D:.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
p={"P2.3",0x20}, r={"P1.5",0x01}, x={"P1.2",0x10},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "CPERR.EXE\n")
|
||||
key(16.0, "P2.1", 0x10) -- Enter после итоговой таблицы.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {14.0, 18.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p5-copy-faults] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 20.0 then
|
||||
print("[p5-copy-faults] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,125 @@
|
||||
-- P5 на HDD D: реальный disk-full при F5 и обязательный cleanup temp.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- левая -> SOURCE.
|
||||
key(17.0, "P1.6", 0x20) -- Tab.
|
||||
key(18.0, "P2.3", 0x10) -- TARGET.
|
||||
key(19.0, "P2.1", 0x10) -- правая -> TARGET.
|
||||
key(21.0, "P1.6", 0x20) -- Tab влево.
|
||||
key(22.0, "P2.3", 0x10) -- FULL.BIN после `..`.
|
||||
key(23.0, "P2.1", 0x40) -- F5, ожидается ENOSPC.
|
||||
|
||||
key(35.0, "P2.3", 0x80) -- F10 после управляемой ошибки.
|
||||
key(37.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
20.5, -- SOURCE/TARGET на почти полном диске;
|
||||
25.0, -- промежуточный progress либо уже ошибка;
|
||||
32.0, -- управляемый ENOSPC, TARGET пуст;
|
||||
36.0, -- диалог выхода после ошибки;
|
||||
40.0 -- чистый prompt DSS.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p5-enospc] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 42.0 then
|
||||
print("[p5-enospc] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,133 @@
|
||||
-- P5: извлечение и возврат тестового HDD D: во время работы Commander.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local HDD_TAG = ":ata2:1:hdd:image"
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- левая панель -> MEDIA.
|
||||
key(17.0, "P2.3", 0x10) -- DEEP.
|
||||
key(17.3, "P2.3", 0x10) -- DATA.BIN.
|
||||
key(19.0, "P2.1", 0x40) -- F5 при отсутствующем HDD.
|
||||
key(26.0, "P2.1", 0x40) -- тот же F5 после возврата HDD.
|
||||
key(34.0, "P2.3", 0x80) -- F10.
|
||||
key(36.0, "P2.1", 0x10) -- подтвердить выход.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {14.0, 17.7, 21.0, 24.5, 31.0, 35.0, 38.0}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
local image_path = nil
|
||||
local media_phase = 0
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
if media_phase == 0 and elapsed >= 18.0 then
|
||||
local hdd = assert(manager.machine.images[HDD_TAG], "hard2 image not found")
|
||||
image_path = hdd.filename
|
||||
hdd:unload()
|
||||
media_phase = 1
|
||||
print("[p5-media] hard2 unloaded, exists=" .. tostring(hdd.exists))
|
||||
elseif media_phase == 1 and elapsed >= 23.0 then
|
||||
local hdd = assert(manager.machine.images[HDD_TAG], "hard2 image not found")
|
||||
local err = hdd:load(image_path)
|
||||
media_phase = 2
|
||||
print("[p5-media] hard2 loaded, exists=" .. tostring(hdd.exists) ..
|
||||
" err=" .. tostring(err))
|
||||
end
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p5-media] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 40.0 then
|
||||
print("[p5-media] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,124 @@
|
||||
-- P5: ровно 27/28 записей и начало прокрутки на 28-й.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- левая -> N27.
|
||||
key(17.0, "P1.6", 0x20) -- Tab.
|
||||
key(18.0, "P2.3", 0x10) -- N28.
|
||||
key(19.0, "P2.1", 0x10) -- правая -> N28.
|
||||
key(23.0, "P2.5", 0x20) -- End: cursor=27, top=1.
|
||||
key(27.0, "P1.6", 0x20) -- Tab: N27.
|
||||
key(28.0, "P2.5", 0x20) -- End: cursor=26, top=0.
|
||||
key(32.0, "P2.3", 0x80) -- F10.
|
||||
key(34.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
20.5, -- count 27/28, обе панели top=0;
|
||||
25.0, -- N28 cursor=27/top=1;
|
||||
30.0, -- N27 cursor=26/top=0;
|
||||
36.0 -- чистый prompt DSS.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p5-panel-edges] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 38.0 then
|
||||
print("[p5-panel-edges] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,159 @@
|
||||
-- P5 на HDD D: 20 copy, 100 refresh и три полных cleanup-цикла.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local function refresh_chord(t)
|
||||
add(t, "P1.1", 0x02, 1)
|
||||
add(t + 0.03, "P1.5", 0x01, 1)
|
||||
add(t + 0.20, "P1.5", 0x01, 0)
|
||||
add(t + 0.23, "P1.1", 0x02, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- левая -> SOURCE.
|
||||
key(17.0, "P1.6", 0x20) -- Tab.
|
||||
key(18.0, "P2.3", 0x10) -- TARGET.
|
||||
key(19.0, "P2.1", 0x10) -- правая -> TARGET.
|
||||
key(21.0, "P1.6", 0x20) -- Tab влево.
|
||||
|
||||
-- В SOURCE: `..`, затем F00.BIN..F19.BIN. Copy не меняет active selection.
|
||||
-- Интервалы растут вместе с числом 4-КБ блоков: ввод во время синхронного
|
||||
-- copy не буферизуется и не должен считаться выполненной командой.
|
||||
local copy_times = {
|
||||
22.0, 24.0, 26.0, 28.0, 30.0, 34.0, 38.0, 40.0, 42.0, 44.0,
|
||||
46.0, 48.0, 50.0, 52.0, 54.0, 57.0, 60.0, 64.0, 68.0, 75.0
|
||||
}
|
||||
for i = 0, 19 do
|
||||
local t = copy_times[i + 1]
|
||||
key(t, "P2.3", 0x10) -- Down.
|
||||
key(t + 0.25, "P2.1", 0x40) -- F5.
|
||||
end
|
||||
|
||||
-- Последний файл больше 1 МБ; оставляем запас до refresh.
|
||||
for i = 0, 99 do
|
||||
refresh_chord(105.0 + i * 0.50)
|
||||
end
|
||||
|
||||
key(160.0, "P2.3", 0x80) -- F10 первого запуска.
|
||||
key(161.5, "P2.1", 0x10)
|
||||
|
||||
type_text(167.0, "SPRCMD .EXE\n")
|
||||
key(173.0, "P2.3", 0x80)
|
||||
key(174.5, "P2.1", 0x10)
|
||||
|
||||
type_text(180.0, "SPRCMD .EXE\n")
|
||||
key(186.0, "P2.3", 0x80)
|
||||
key(187.5, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
14.0, -- root D:;
|
||||
20.5, -- SOURCE/TARGET;
|
||||
80.0, -- прогресс последнего файла больше 1 МБ;
|
||||
100.0, -- 20 файлов показаны в TARGET;
|
||||
157.0, -- состояние после 100 refresh;
|
||||
164.0, -- первый cleanup, чистый prompt;
|
||||
177.0, -- второй cleanup;
|
||||
190.0 -- третий cleanup.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p5-stability] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 192.0 then
|
||||
print("[p5-stability] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,127 @@
|
||||
-- P5: запуск реального mdview2, переключение вида и возврат в Commander.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80},
|
||||
["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10},
|
||||
[":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch]
|
||||
local shifted = false
|
||||
if not def then
|
||||
local lower = ch:lower()
|
||||
def = chars[lower]
|
||||
shifted = ch ~= lower
|
||||
else
|
||||
shifted = def[3] or false
|
||||
end
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SPRCMD .EXE\n")
|
||||
|
||||
key(15.0, "P2.1", 0x10) -- Commander -> VIEWER.
|
||||
key(17.0, "P2.3", 0x10) -- MDVIEW2.EXE после `..`.
|
||||
key(18.0, "P2.1", 0x10) -- Enter/EXEC; README.MD берётся из child CWD.
|
||||
|
||||
key(28.0, "P1.2", 0x20) -- PgDn внутри mdview2.
|
||||
key(31.0, "P1.2", 0x80) -- F2: RAW.
|
||||
key(36.0, "P2.3", 0x80) -- F10: выход из mdview2.
|
||||
|
||||
key(44.0, "P2.3", 0x80) -- F10 Commander.
|
||||
key(46.0, "P2.1", 0x10)
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
16.0, -- Commander в VIEWER;
|
||||
25.0, -- реальный MD-экран mdview2;
|
||||
29.5, -- mdview2 после PgDn;
|
||||
33.5, -- RAW-режим mdview2;
|
||||
40.0, -- Commander восстановлен после viewer;
|
||||
45.0, -- диалог выхода Commander;
|
||||
49.0 -- чистый prompt DSS.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p5-viewer] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 51.0 then
|
||||
print("[p5-viewer] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,143 @@
|
||||
-- 0.2/P6: четыре сортировки, reverse, независимость панелей и refresh.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
m={"P2.0",0x10}, o={"P2.2",0x02}, p={"P2.3",0x20},
|
||||
r={"P1.5",0x01}, s={"P1.2",0x08}, t={"P1.5",0x20},
|
||||
x={"P1.2",0x10}, [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
local function ctrl_f(t, tag, mask)
|
||||
chord(t, "P1.1", 0x02, tag, mask)
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SORTRT.EXE\n")
|
||||
type_text(12.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(18.0, "P2.1", 0x10) -- left -> SORTL.
|
||||
key(20.0, "P1.6", 0x20) -- Tab -> right.
|
||||
key(21.0, "P2.3", 0x10) -- Down -> SORTR.
|
||||
key(22.0, "P2.1", 0x10) -- right -> SORTR.
|
||||
key(24.0, "P1.6", 0x20) -- left active again.
|
||||
|
||||
ctrl_f(27.0, "P2.6", 0x80) -- Ctrl+F4: extension ascending.
|
||||
ctrl_f(31.0, "P2.6", 0x80) -- extension descending.
|
||||
ctrl_f(35.0, "P2.1", 0x80) -- Ctrl+F6: size ascending.
|
||||
ctrl_f(39.0, "P2.1", 0x80) -- size descending.
|
||||
ctrl_f(43.0, "P2.1", 0x40) -- Ctrl+F5: date ascending.
|
||||
ctrl_f(47.0, "P2.1", 0x40) -- date descending.
|
||||
ctrl_f(51.0, "P2.6", 0x40) -- Ctrl+F3: name ascending.
|
||||
ctrl_f(55.0, "P2.6", 0x40) -- name descending.
|
||||
|
||||
ctrl_f(59.0, "P2.1", 0x80) -- left size ascending.
|
||||
ctrl_f(61.0, "P2.1", 0x80) -- left size descending.
|
||||
key(63.0, "P1.6", 0x20) -- right active, still N+.
|
||||
ctrl_f(65.0, "P2.6", 0x80) -- right extension ascending.
|
||||
chord(68.0, "P1.1", 0x02, "P1.5", 0x01) -- refresh keeps E+.
|
||||
|
||||
key(72.0, "P2.3", 0x80) -- F10.
|
||||
key(74.0, "P2.1", 0x10) -- Enter: cleanup.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
25.5, -- обе панели N+;
|
||||
29.0, -- left E+;
|
||||
33.0, -- left E-;
|
||||
37.0, -- left S+;
|
||||
41.0, -- left S-;
|
||||
45.0, -- left D+;
|
||||
49.0, -- left D-;
|
||||
53.0, -- left N+;
|
||||
57.0, -- left N-;
|
||||
66.5, -- left S-, right E+;
|
||||
70.0, -- refresh сохранил right E+;
|
||||
76.0 -- чистый DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p6-sort] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 78.0 then
|
||||
print("[p6-sort] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,148 @@
|
||||
-- 0.2/P7: Insert, invert, перенос flags через sort и сброс на refresh.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
b={"P1.5",0x10}, c={"P1.4",0x08}, d={"P1.4",0x04},
|
||||
e={"P1.4",0x20}, i={"P1.4",0x01}, l={"P2.2",0x08},
|
||||
m={"P2.0",0x10}, n={"P2.0",0x08}, p={"P2.3",0x20},
|
||||
r={"P1.5",0x01}, s={"P1.2",0x08}, t={"P1.5",0x20},
|
||||
x={"P1.2",0x10}, [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "SELRT.EXE\n")
|
||||
type_text(12.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(18.0, "P2.1", 0x10) -- left -> SELECT.
|
||||
key(21.0, "P2.6", 0x01) -- Insert на `..`: запрещён.
|
||||
key(23.0, "P2.3", 0x10) -- Down -> ADIR.
|
||||
key(24.0, "P2.6", 0x01) -- выбрать ADIR, cursor -> ZDIR.
|
||||
key(26.0, "P2.6", 0x01) -- выбрать ZDIR, cursor -> A.BIN.
|
||||
key(28.0, "P2.6", 0x01) -- выбрать A.BIN, cursor -> B.BIN.
|
||||
chord(32.0, "P1.1", 0x02, "P2.6", 0x40) -- Ctrl+F3: N-.
|
||||
chord(36.0, "P1.7", 0x02, "P1.4", 0x80) -- Shift+8 = `*`.
|
||||
chord(40.0, "P1.1", 0x02, "P1.5", 0x01) -- Ctrl+R: выбор сброшен.
|
||||
|
||||
chord(43.0, "P1.7", 0x02, "P2.1", 0x01) -- Shift+=: `+`.
|
||||
key(44.0, "P2.1", 0x20) -- стереть default `*.*`.
|
||||
key(44.3, "P2.1", 0x20)
|
||||
key(44.6, "P2.1", 0x20)
|
||||
chord(45.0, "P1.7", 0x02, "P1.4", 0x80) -- `*`.
|
||||
key(45.3, "P2.2", 0x10) -- `.`.
|
||||
type_text(45.6, "bin")
|
||||
key(46.4, "P2.1", 0x10) -- apply `*.bin`.
|
||||
|
||||
key(50.0, "P2.2", 0x20) -- `-`.
|
||||
key(51.0, "P2.1", 0x20) -- стереть default `*.*`.
|
||||
key(51.3, "P2.1", 0x20)
|
||||
key(51.6, "P2.1", 0x20)
|
||||
type_text(52.0, "b.bin")
|
||||
key(53.0, "P2.1", 0x10) -- unselect B.BIN.
|
||||
|
||||
chord(56.0, "P1.7", 0x02, "P2.1", 0x01) -- `+`, затем cancel.
|
||||
key(57.0, "P1.6", 0x40) -- Esc.
|
||||
key(60.0, "P2.3", 0x80) -- F10.
|
||||
key(62.0, "P2.1", 0x10) -- Enter: cleanup.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
20.0, -- исходный список;
|
||||
22.0, -- `..` не выбран;
|
||||
29.5, -- два каталога и A.BIN выбраны, sel=10/003;
|
||||
33.5, -- sort N- сохранил flags и текущий B.BIN;
|
||||
37.5, -- invert оставил каталоги и B.BIN/C.BIN, sel=50/004;
|
||||
41.5, -- refresh сбросил выбор, N- сохранён;
|
||||
43.5, -- mask dialog с default `*.*`;
|
||||
46.1, -- отредактировано `*.bin`;
|
||||
48.0, -- маска выбрала три BIN, sel=60/003;
|
||||
54.5, -- `- b.bin` оставил A/C, sel=40/002;
|
||||
58.0, -- cancel следующего `+` сохранил выбор;
|
||||
64.0 -- чистый DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p7-select] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 66.0 then
|
||||
print("[p7-select] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,136 @@
|
||||
-- 0.2/P9: F7 success, duplicate, invalid 8.3, cancel и навигация.
|
||||
|
||||
local KB = ":kbd:ms_naturl:"
|
||||
local HOLD = 0.06
|
||||
local events = {}
|
||||
|
||||
local function add(t, tag, mask, value)
|
||||
events[#events + 1] = {t, KB .. tag, mask, value}
|
||||
end
|
||||
|
||||
local function key(t, tag, mask)
|
||||
add(t, tag, mask, 1)
|
||||
add(t + HOLD, tag, mask, 0)
|
||||
end
|
||||
|
||||
local function chord(t, mod_tag, mod_mask, key_tag, key_mask)
|
||||
add(t, mod_tag, mod_mask, 1)
|
||||
add(t + 0.02, key_tag, key_mask, 1)
|
||||
add(t + 0.08, key_tag, key_mask, 0)
|
||||
add(t + 0.10, mod_tag, mod_mask, 0)
|
||||
end
|
||||
|
||||
local chars = {
|
||||
c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20},
|
||||
g={"P1.5",0x04}, i={"P1.4",0x01}, k={"P1.4",0x02},
|
||||
l={"P2.2",0x08}, m={"P2.0",0x10}, n={"P2.0",0x08},
|
||||
o={"P2.2",0x02}, p={"P2.3",0x20}, r={"P1.5",0x01},
|
||||
s={"P1.2",0x08}, t={"P1.5",0x20}, w={"P1.2",0x04},
|
||||
x={"P1.2",0x10}, ["9"]={"P2.2",0x01},
|
||||
[" "]={"P2.4",0x80}, ["."]={"P2.2",0x10},
|
||||
["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true}
|
||||
}
|
||||
|
||||
local function type_text(t, value)
|
||||
for i = 1, #value do
|
||||
local ch = value:sub(i, i)
|
||||
local def = chars[ch] or chars[ch:lower()]
|
||||
local shifted = def[3] or ch ~= ch:lower()
|
||||
if shifted then
|
||||
chord(t, "P1.7", 0x02, def[1], def[2])
|
||||
else
|
||||
key(t, def[1], def[2])
|
||||
end
|
||||
t = t + 0.14
|
||||
end
|
||||
end
|
||||
|
||||
type_text(8.0, "d:\n")
|
||||
type_text(9.0, "MKRT.EXE\n")
|
||||
type_text(12.0, "SPRCMD.EXE\n")
|
||||
|
||||
key(18.0, "P2.1", 0x10) -- left -> MKTEST.
|
||||
key(20.0, "P2.2", 0x40) -- F7.
|
||||
type_text(21.0, "newdir")
|
||||
key(22.2, "P2.1", 0x10) -- create.
|
||||
|
||||
key(26.0, "P2.2", 0x40) -- F7 duplicate.
|
||||
type_text(27.0, "newdir")
|
||||
key(28.2, "P2.1", 0x10)
|
||||
|
||||
key(32.0, "P2.2", 0x40) -- F7 invalid basename > 8.
|
||||
type_text(33.0, "toolong99")
|
||||
key(34.5, "P2.1", 0x10)
|
||||
|
||||
key(38.0, "P2.2", 0x40) -- F7 then cancel.
|
||||
key(39.0, "P1.6", 0x40)
|
||||
key(42.0, "P2.3", 0x10) -- Down -> NEWDIR.
|
||||
key(43.0, "P2.1", 0x10) -- enter NEWDIR.
|
||||
key(47.0, "P2.1", 0x20) -- Backspace -> MKTEST.
|
||||
key(51.0, "P2.3", 0x80) -- F10.
|
||||
key(53.0, "P2.1", 0x10) -- cleanup.
|
||||
|
||||
table.sort(events, function(a, b) return a[1] < b[1] end)
|
||||
|
||||
local snap_times = {
|
||||
19.0, -- исходный MKTEST;
|
||||
20.5, -- пустой F7 dialog;
|
||||
23.5, -- NEWDIR создан;
|
||||
29.5, -- duplicate managed error;
|
||||
35.5, -- invalid 8.3 rejected;
|
||||
40.0, -- cancel не изменил каталог;
|
||||
44.5, -- вошли в NEWDIR;
|
||||
48.5, -- вернулись в MKTEST;
|
||||
55.0 -- чистый DSS prompt.
|
||||
}
|
||||
local snaps_done = {}
|
||||
local start_time = nil
|
||||
local ports = nil
|
||||
local index = 1
|
||||
local field_cache = {}
|
||||
|
||||
local function now()
|
||||
local t = manager.machine.time
|
||||
return t.seconds + t.attoseconds / 1e18
|
||||
end
|
||||
|
||||
local function field(tag, mask)
|
||||
local cache_key = tag .. "/" .. mask
|
||||
if field_cache[cache_key] then return field_cache[cache_key] end
|
||||
for _, candidate in pairs(ports[tag].fields) do
|
||||
if candidate.mask == mask then
|
||||
field_cache[cache_key] = candidate
|
||||
return candidate
|
||||
end
|
||||
end
|
||||
error("Не найдено поле " .. cache_key)
|
||||
end
|
||||
|
||||
emu.add_machine_reset_notifier(function()
|
||||
start_time = now()
|
||||
end)
|
||||
|
||||
emu.register_periodic(function()
|
||||
if not start_time then return end
|
||||
if not ports then ports = manager.machine.ioport.ports end
|
||||
local elapsed = now() - start_time
|
||||
|
||||
while index <= #events and elapsed >= events[index][1] do
|
||||
local event = events[index]
|
||||
field(event[2], event[3]):set_value(event[4])
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
for _, snap_time in ipairs(snap_times) do
|
||||
if not snaps_done[snap_time] and elapsed >= snap_time then
|
||||
snaps_done[snap_time] = true
|
||||
manager.machine.video:snapshot()
|
||||
print("[p9-mkdir] snapshot t=" .. elapsed)
|
||||
end
|
||||
end
|
||||
|
||||
if elapsed >= 57.0 then
|
||||
print("[p9-mkdir] exit t=" .. elapsed)
|
||||
manager.machine:exit()
|
||||
end
|
||||
end)
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture команды F6 local rename."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
panel = root / "RENTEST"
|
||||
panel.mkdir(parents=True)
|
||||
(panel / "OLD.TXT").write_bytes(b"old payload\r\n")
|
||||
(panel / "EXIST.TXT").write_bytes(b"existing payload\r\n")
|
||||
(panel / "UIOLD.TXT").write_bytes(b"ui payload\r\n")
|
||||
old_dir = panel / "OLDDIR"
|
||||
old_dir.mkdir()
|
||||
(old_dir / "KEEP.BIN").write_bytes(b"dir payload\r\n")
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
expected = [
|
||||
"PASS CWD captured before rename matrix",
|
||||
"PASS file renamed without data change",
|
||||
"PASS existing target blocks rename and stays intact",
|
||||
"PASS directory renamed with contents intact",
|
||||
"PASS failed directory change preserves CWD",
|
||||
"PASS successful and rejected renames restore CWD",
|
||||
"ALL PASS",
|
||||
]
|
||||
failures: list[str] = []
|
||||
result = root / "RENRES.TXT"
|
||||
panel = root / "RENTEST"
|
||||
|
||||
if not result.is_file():
|
||||
failures.append("нет RENRES.TXT")
|
||||
else:
|
||||
text = result.read_bytes().decode("cp866", errors="replace")
|
||||
for line in expected:
|
||||
if line not in text:
|
||||
failures.append(f"нет строки результата: {line}")
|
||||
|
||||
files = {
|
||||
"NEW.TXT": b"old payload\r\n",
|
||||
"EXIST.TXT": b"existing payload\r\n",
|
||||
"FINAL.BIN": b"ui payload\r\n",
|
||||
"NEWDIR/KEEP.BIN": b"dir payload\r\n",
|
||||
}
|
||||
for relative, content in files.items():
|
||||
path = panel / relative
|
||||
if not path.is_file() or path.read_bytes() != content:
|
||||
failures.append(f"неверное содержимое {relative}")
|
||||
for unwanted in ("OLD.TXT", "UIOLD.TXT", "OLDDIR"):
|
||||
if (panel / unwanted).exists():
|
||||
failures.append(f"старое имя осталось: {unwanted}")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: file/dir rename, no-overwrite, CWD restore и UI F6.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* p10_rename_probe.c — target-регрессия локального F6/RENAME.
|
||||
*
|
||||
* Проверяет файл, каталог, запрет overwrite и восстановление CWD через тот
|
||||
* же низкоуровневый helper, который вызывает Commander.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include "sc_file_ops.h"
|
||||
|
||||
static int result_fd = -1;
|
||||
static uint8_t failures;
|
||||
static char cwd_before[256];
|
||||
static char cwd_after[256];
|
||||
static char cwd_scratch[256];
|
||||
|
||||
static void log_text(const char *text)
|
||||
{
|
||||
const char *end = text;
|
||||
while (*end != '\0') end++;
|
||||
if (result_fd >= 0) {
|
||||
(void)write(result_fd, text, (uint16_t)(end - text));
|
||||
}
|
||||
}
|
||||
|
||||
static void check(uint8_t condition, const char *name)
|
||||
{
|
||||
log_text(condition ? "PASS " : "FAIL ");
|
||||
if (!condition) failures++;
|
||||
log_text(name);
|
||||
log_text("\r\n");
|
||||
}
|
||||
|
||||
static uint8_t file_has(const char *path, const char *expected)
|
||||
{
|
||||
char data[24];
|
||||
int fd = open(path, O_RDONLY);
|
||||
int got;
|
||||
uint16_t length = 0u;
|
||||
|
||||
if (fd < 0) return 0u;
|
||||
while (expected[length] != '\0') length++;
|
||||
got = read(fd, data, sizeof(data));
|
||||
(void)close(fd);
|
||||
return got == (int)length && memcmp(data, expected, length) == 0;
|
||||
}
|
||||
|
||||
static uint8_t file_absent(const char *path)
|
||||
{
|
||||
int fd;
|
||||
errno = 0;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
(void)close(fd);
|
||||
return 0u;
|
||||
}
|
||||
return errno == ENOENT || errno == ENOPATH;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int rc;
|
||||
int collision_errno;
|
||||
int entered;
|
||||
|
||||
result_fd = open("D:\\RENRES.TXT", O_WRONLY | O_CREAT | O_TRUNC);
|
||||
check(getcwd(cwd_before, sizeof(cwd_before)) != 0,
|
||||
"CWD captured before rename matrix");
|
||||
|
||||
rc = sc_rename_local("D:\\RENTEST", "OLD.TXT", "NEW.TXT",
|
||||
cwd_scratch, sizeof(cwd_scratch));
|
||||
check(rc == 0 && file_absent("D:\\RENTEST\\OLD.TXT") &&
|
||||
file_has("D:\\RENTEST\\NEW.TXT", "old payload\r\n"),
|
||||
"file renamed without data change");
|
||||
|
||||
errno = 0;
|
||||
rc = sc_rename_local("D:\\RENTEST", "NEW.TXT", "EXIST.TXT",
|
||||
cwd_scratch, sizeof(cwd_scratch));
|
||||
collision_errno = errno;
|
||||
check(rc != 0 && collision_errno == EEXIST &&
|
||||
file_has("D:\\RENTEST\\NEW.TXT", "old payload\r\n") &&
|
||||
file_has("D:\\RENTEST\\EXIST.TXT", "existing payload\r\n"),
|
||||
"existing target blocks rename and stays intact");
|
||||
|
||||
rc = sc_rename_local("D:\\RENTEST", "OLDDIR", "NEWDIR",
|
||||
cwd_scratch, sizeof(cwd_scratch));
|
||||
entered = chdir("D:\\RENTEST\\NEWDIR");
|
||||
if (entered == 0) entered = chdir(cwd_before);
|
||||
check(rc == 0 && entered == 0 &&
|
||||
file_has("D:\\RENTEST\\NEWDIR\\KEEP.BIN", "dir payload\r\n"),
|
||||
"directory renamed with contents intact");
|
||||
|
||||
errno = 0;
|
||||
rc = sc_rename_local("D:\\MISSING", "A.TXT", "B.TXT",
|
||||
cwd_scratch, sizeof(cwd_scratch));
|
||||
check(rc != 0 && getcwd(cwd_after, sizeof(cwd_after)) != 0 &&
|
||||
strcmp(cwd_before, cwd_after) == 0,
|
||||
"failed directory change preserves CWD");
|
||||
check(getcwd(cwd_after, sizeof(cwd_after)) != 0 &&
|
||||
strcmp(cwd_before, cwd_after) == 0,
|
||||
"successful and rejected renames restore CWD");
|
||||
|
||||
if (failures == 0u) log_text("ALL PASS\r\n");
|
||||
if (result_fd >= 0) (void)close(result_fd);
|
||||
return failures;
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture команды F8 delete."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
panel = root / "DELTEST"
|
||||
panel.mkdir(parents=True)
|
||||
(panel / "TARGET.TXT").write_bytes(b"target payload\r\n")
|
||||
(panel / "REMOVE.TXT").write_bytes(b"remove payload\r\n")
|
||||
(panel / "KEEP.TXT").write_bytes(b"keep remains\r\n")
|
||||
(panel / "TEMPDIR").mkdir()
|
||||
(panel / "EMPTYDIR").mkdir()
|
||||
nonempty = panel / "NONEMPTY"
|
||||
nonempty.mkdir()
|
||||
(nonempty / "CHILD.TXT").write_bytes(b"child remains\r\n")
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
expected = [
|
||||
"PASS CWD captured before delete matrix",
|
||||
"PASS unlink removes one regular file",
|
||||
"PASS rmdir removes one empty directory",
|
||||
"PASS nonempty directory is not removed",
|
||||
"PASS unrelated file remains intact",
|
||||
"PASS full-path delete operations preserve CWD",
|
||||
"ALL PASS",
|
||||
]
|
||||
failures: list[str] = []
|
||||
result = root / "DELRES.TXT"
|
||||
panel = root / "DELTEST"
|
||||
|
||||
if not result.is_file():
|
||||
failures.append("нет DELRES.TXT")
|
||||
else:
|
||||
text = result.read_bytes().decode("cp866", errors="replace")
|
||||
for line in expected:
|
||||
if line not in text:
|
||||
failures.append(f"нет строки результата: {line}")
|
||||
|
||||
absent = ("TARGET.TXT", "TEMPDIR", "REMOVE.TXT", "EMPTYDIR")
|
||||
for name in absent:
|
||||
if (panel / name).exists():
|
||||
failures.append(f"объект не удалён: {name}")
|
||||
remaining = {
|
||||
"KEEP.TXT": b"keep remains\r\n",
|
||||
"NONEMPTY/CHILD.TXT": b"child remains\r\n",
|
||||
}
|
||||
for name, content in remaining.items():
|
||||
path = panel / name
|
||||
if not path.is_file() or path.read_bytes() != content:
|
||||
failures.append(f"объект изменён: {name}")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: file/empty-dir delete, cancel, guards, nonempty и CWD.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,85 @@
|
||||
/* p11_delete_probe.c — target-регрессия однообъектного F8 delete. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int result_fd = -1;
|
||||
static uint8_t failures;
|
||||
static char cwd_before[256];
|
||||
static char cwd_after[256];
|
||||
|
||||
static void log_text(const char *text)
|
||||
{
|
||||
const char *end = text;
|
||||
while (*end != '\0') end++;
|
||||
if (result_fd >= 0) {
|
||||
(void)write(result_fd, text, (uint16_t)(end - text));
|
||||
}
|
||||
}
|
||||
|
||||
static void check(uint8_t condition, const char *name)
|
||||
{
|
||||
log_text(condition ? "PASS " : "FAIL ");
|
||||
if (!condition) failures++;
|
||||
log_text(name);
|
||||
log_text("\r\n");
|
||||
}
|
||||
|
||||
static uint8_t file_has(const char *path, const char *expected)
|
||||
{
|
||||
char data[24];
|
||||
int fd = open(path, O_RDONLY);
|
||||
int got;
|
||||
uint16_t length = 0u;
|
||||
|
||||
if (fd < 0) return 0u;
|
||||
while (expected[length] != '\0') length++;
|
||||
got = read(fd, data, sizeof(data));
|
||||
(void)close(fd);
|
||||
return got == (int)length && memcmp(data, expected, length) == 0;
|
||||
}
|
||||
|
||||
static uint8_t file_absent(const char *path)
|
||||
{
|
||||
int fd;
|
||||
errno = 0;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
(void)close(fd);
|
||||
return 0u;
|
||||
}
|
||||
return errno == ENOENT || errno == ENOPATH;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
result_fd = open("D:\\DELRES.TXT", O_WRONLY | O_CREAT | O_TRUNC);
|
||||
check(getcwd(cwd_before, sizeof(cwd_before)) != 0,
|
||||
"CWD captured before delete matrix");
|
||||
check(unlink("D:\\DELTEST\\TARGET.TXT") == 0 &&
|
||||
file_absent("D:\\DELTEST\\TARGET.TXT"),
|
||||
"unlink removes one regular file");
|
||||
check(rmdir("D:\\DELTEST\\TEMPDIR") == 0,
|
||||
"rmdir removes one empty directory");
|
||||
|
||||
errno = 0;
|
||||
rc = rmdir("D:\\DELTEST\\NONEMPTY");
|
||||
check(rc != 0 &&
|
||||
file_has("D:\\DELTEST\\NONEMPTY\\CHILD.TXT",
|
||||
"child remains\r\n"),
|
||||
"nonempty directory is not removed");
|
||||
check(file_has("D:\\DELTEST\\KEEP.TXT", "keep remains\r\n"),
|
||||
"unrelated file remains intact");
|
||||
check(getcwd(cwd_after, sizeof(cwd_after)) != 0 &&
|
||||
strcmp(cwd_before, cwd_after) == 0,
|
||||
"full-path delete operations preserve CWD");
|
||||
|
||||
if (failures == 0u) log_text("ALL PASS\r\n");
|
||||
if (result_fd >= 0) (void)close(result_fd);
|
||||
return failures;
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture группового F5."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(size: int, seed: int) -> bytes:
|
||||
return bytes(((index * 37 + seed) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
source = root / "GRSRC"
|
||||
target = root / "GRDST"
|
||||
source.mkdir(parents=True)
|
||||
target.mkdir()
|
||||
(source / "A.BIN").write_bytes(payload(10, 3))
|
||||
(source / "B.BIN").write_bytes(payload(4097, 11))
|
||||
(source / "C.BIN").write_bytes(b"")
|
||||
directory = source / "ADIR"
|
||||
directory.mkdir()
|
||||
(directory / "INSIDE.TXT").write_bytes(b"must not be copied\r\n")
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
source = root / "GRSRC"
|
||||
target = root / "GRDST"
|
||||
failures: list[str] = []
|
||||
expected = {
|
||||
"A.BIN": payload(10, 3),
|
||||
"B.BIN": payload(4097, 11),
|
||||
"C.BIN": b"",
|
||||
}
|
||||
|
||||
for name, content in expected.items():
|
||||
source_path = source / name
|
||||
target_path = target / name
|
||||
if not source_path.is_file() or source_path.read_bytes() != content:
|
||||
failures.append(f"source изменён: {name}")
|
||||
if not target_path.is_file() or target_path.read_bytes() != content:
|
||||
failures.append(f"target не совпадает: {name}")
|
||||
target_inside = target / "ADIR" / "INSIDE.TXT"
|
||||
if (not target_inside.is_file() or
|
||||
target_inside.read_bytes() != b"must not be copied\r\n"):
|
||||
failures.append("выбранный каталог не скопирован рекурсивно")
|
||||
temporary = [path.name for path in target.rglob("*")
|
||||
if path.name.upper().endswith(".TMP")]
|
||||
if temporary:
|
||||
failures.append(f"остались temp: {temporary}")
|
||||
inside = source / "ADIR" / "INSIDE.TXT"
|
||||
if not inside.is_file() or inside.read_bytes() != b"must not be copied\r\n":
|
||||
failures.append("исходный выбранный каталог изменён")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: 3 selected files and selected directory copied; no temp.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture группового F8."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
panel = root / "GDEL"
|
||||
panel.mkdir(parents=True)
|
||||
(panel / "A.TXT").write_bytes(b"delete A\r\n")
|
||||
(panel / "B.BIN").write_bytes(b"delete B payload\r\n")
|
||||
(panel / "KEEP.TXT").write_bytes(b"keep payload\r\n")
|
||||
directory = panel / "ADIR"
|
||||
directory.mkdir()
|
||||
(directory / "INSIDE.TXT").write_bytes(b"directory payload\r\n")
|
||||
(panel / "EMPTYDIR").mkdir()
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
panel = root / "GDEL"
|
||||
failures: list[str] = []
|
||||
|
||||
for name in ("A.TXT", "B.BIN"):
|
||||
if (panel / name).exists():
|
||||
failures.append(f"выбранный файл не удалён: {name}")
|
||||
expected = {
|
||||
"KEEP.TXT": b"keep payload\r\n",
|
||||
"ADIR/INSIDE.TXT": b"directory payload\r\n",
|
||||
}
|
||||
for name, content in expected.items():
|
||||
path = panel / name
|
||||
if not path.is_file() or path.read_bytes() != content:
|
||||
failures.append(f"сохранённый объект изменён: {name}")
|
||||
if not (panel / "EMPTYDIR").is_dir():
|
||||
failures.append("выбранный пустой каталог ошибочно удалён")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: selected files deleted; selected directories and KEEP preserved.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture стандартных цветов имён."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
CONTENTS = {
|
||||
"APP.EXE": b"EXE colour sample\r\n",
|
||||
"ARCHIVE.ZIP": b"ZIP colour sample\r\n",
|
||||
"NOEXT": b"no extension sample\r\n",
|
||||
"README.TXT": b"text colour sample\r\n",
|
||||
"SUBDIR/INSIDE.DAT": b"directory sample\r\n",
|
||||
}
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
panel = root / "COLORS"
|
||||
for name, content in CONTENTS.items():
|
||||
path = panel / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(content)
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
panel = root / "COLORS"
|
||||
failures: list[str] = []
|
||||
for name, content in CONTENTS.items():
|
||||
path = panel / name
|
||||
if not path.is_file() or path.read_bytes() != content:
|
||||
failures.append(f"объект отсутствует или изменён: {name}")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: colour fixture preserved.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет fixture отмены рекурсивного F5."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(size: int) -> bytes:
|
||||
return bytes(((index * 29 + 17) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
BIG = payload(4 * 1024 * 1024)
|
||||
AFTER = b"may be committed before traversal reaches BIG\r\n"
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
tree = root / "CSRC" / "TREE"
|
||||
tree.mkdir(parents=True)
|
||||
(root / "CDST").mkdir()
|
||||
(tree / "BIG.BIN").write_bytes(BIG)
|
||||
(tree / "AFTER.TXT").write_bytes(AFTER)
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
source = root / "CSRC" / "TREE"
|
||||
target = root / "CDST" / "TREE"
|
||||
failures: list[str] = []
|
||||
|
||||
if (source / "BIG.BIN").read_bytes() != BIG:
|
||||
failures.append("source BIG.BIN изменён")
|
||||
if (source / "AFTER.TXT").read_bytes() != AFTER:
|
||||
failures.append("source AFTER.TXT изменён")
|
||||
if not target.is_dir():
|
||||
failures.append("root target-каталог не был создан до cancel")
|
||||
else:
|
||||
big_target = target / "BIG.BIN"
|
||||
if big_target.exists():
|
||||
failures.append("BIG.BIN committed несмотря на ранний cancel")
|
||||
after_target = target / "AFTER.TXT"
|
||||
if after_target.exists() and after_target.read_bytes() != AFTER:
|
||||
failures.append("частично завершённый AFTER.TXT повреждён")
|
||||
temporary = [path.name for path in target.iterdir()
|
||||
if path.name.upper().endswith(".TMP")]
|
||||
if temporary:
|
||||
failures.append(f"после cancel остались temp: {temporary}")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: tree cancel removed active temp; source intact; no BIG commit.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture рекурсивного F5 одного каталога."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(size: int, seed: int) -> bytes:
|
||||
return bytes(((index * 53 + seed) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
tree = root / "SRC" / "TREE"
|
||||
(root / "DST").mkdir(parents=True)
|
||||
(tree / "EMPTY").mkdir(parents=True)
|
||||
(tree / "ROOT.TXT").write_bytes(b"recursive root\r\n")
|
||||
sub = tree / "SUB"
|
||||
sub.mkdir()
|
||||
(sub / "A.BIN").write_bytes(payload(4097, 7))
|
||||
(sub / "ZERO.DAT").write_bytes(b"")
|
||||
|
||||
deep = sub
|
||||
for level in range(1, 17):
|
||||
deep = deep / f"L{level:02d}"
|
||||
deep.mkdir()
|
||||
(deep / "BOTTOM.TXT").write_bytes(b"depth sixteen\r\n")
|
||||
|
||||
|
||||
def tree_snapshot(root: pathlib.Path) -> dict[str, bytes | None]:
|
||||
result: dict[str, bytes | None] = {}
|
||||
for path in sorted(root.rglob("*")):
|
||||
relative = path.relative_to(root).as_posix().upper()
|
||||
result[relative] = None if path.is_dir() else path.read_bytes()
|
||||
return result
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
source = root / "SRC" / "TREE"
|
||||
target = root / "DST" / "TREE"
|
||||
failures: list[str] = []
|
||||
|
||||
if not source.is_dir() or not target.is_dir():
|
||||
failures.append("source или target TREE отсутствует")
|
||||
elif tree_snapshot(source) != tree_snapshot(target):
|
||||
failures.append("рекурсивное дерево target не совпадает с source")
|
||||
|
||||
temporary = [path.as_posix() for path in (root / "DST").rglob("*")
|
||||
if path.name.upper().endswith(".TMP")]
|
||||
if temporary:
|
||||
failures.append(f"остались temp: {temporary}")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: recursive tree copied exactly; depth=16; no temp.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет fixture запрета target внутри source."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
CONTENT = b"self-target guard\r\n"
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
tree = root / "SRC" / "TREE"
|
||||
(tree / "INNER").mkdir(parents=True)
|
||||
(tree / "FILE.TXT").write_bytes(CONTENT)
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
tree = root / "SRC" / "TREE"
|
||||
failures: list[str] = []
|
||||
if (tree / "FILE.TXT").read_bytes() != CONTENT:
|
||||
failures.append("source FILE.TXT изменён")
|
||||
inner = tree / "INNER"
|
||||
if not inner.is_dir() or any(inner.iterdir()):
|
||||
failures.append("внутри source появился рекурсивный target")
|
||||
temporary = [path.as_posix() for path in tree.rglob("*")
|
||||
if path.name.upper().endswith(".TMP")]
|
||||
if temporary:
|
||||
failures.append(f"остались temp: {temporary}")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: target-inside-source refused before mutation.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет group F5 с несколькими directory roots."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(size: int, seed: int) -> bytes:
|
||||
return bytes(((index * 41 + seed) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
source = root / "GSRC"
|
||||
target = root / "GDST"
|
||||
target.mkdir(parents=True)
|
||||
adir = source / "ADIR"
|
||||
bdir = source / "BDIR"
|
||||
(adir / "EMPTY").mkdir(parents=True)
|
||||
bdir.mkdir(parents=True)
|
||||
(adir / "A1.BIN").write_bytes(payload(10, 3))
|
||||
(bdir / "B1.TXT").write_bytes(b"second root\r\n")
|
||||
(source / "ROOT.BIN").write_bytes(payload(4097, 9))
|
||||
(source / "KEEP.TXT").write_bytes(b"not selected\r\n")
|
||||
|
||||
|
||||
def snapshot(root: pathlib.Path) -> dict[str, bytes | None]:
|
||||
result: dict[str, bytes | None] = {}
|
||||
for path in sorted(root.rglob("*")):
|
||||
name = path.relative_to(root).as_posix().upper()
|
||||
result[name] = None if path.is_dir() else path.read_bytes()
|
||||
return result
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
source = root / "GSRC"
|
||||
target = root / "GDST"
|
||||
failures: list[str] = []
|
||||
expected = {
|
||||
"ADIR": None,
|
||||
"ADIR/A1.BIN": payload(10, 3),
|
||||
"ADIR/EMPTY": None,
|
||||
"BDIR": None,
|
||||
"BDIR/B1.TXT": b"second root\r\n",
|
||||
"ROOT.BIN": payload(4097, 9),
|
||||
}
|
||||
if snapshot(target) != expected:
|
||||
failures.append("target не равен двум выбранным деревьям и ROOT.BIN")
|
||||
if not (source / "KEEP.TXT").is_file():
|
||||
failures.append("source KEEP.TXT отсутствует")
|
||||
if (target / "KEEP.TXT").exists():
|
||||
failures.append("невыбранный KEEP.TXT ошибочно скопирован")
|
||||
temporary = [path.as_posix() for path in target.rglob("*")
|
||||
if path.name.upper().endswith(".TMP")]
|
||||
if temporary:
|
||||
failures.append(f"остались temp: {temporary}")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: selected file and two directory roots copied exactly.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет runtime-cancel рекурсивного группового F8."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
DIRECTORY_COUNT = 180
|
||||
|
||||
|
||||
def payload(index: int) -> bytes:
|
||||
return bytes(((offset * 17 + index) & 0xFF) for offset in range(97))
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
panel = root / "CDEL"
|
||||
tree = panel / "TREE"
|
||||
tree.mkdir(parents=True)
|
||||
|
||||
for index in range(DIRECTORY_COUNT):
|
||||
child = tree / f"D{index:03d}"
|
||||
child.mkdir()
|
||||
(child / "ITEM.BIN").write_bytes(payload(index))
|
||||
|
||||
(panel / "TOP.BIN").write_bytes(b"top-level selected file\r\n")
|
||||
(panel / "KEEP.TXT").write_bytes(b"unselected cancel sentinel\r\n")
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
panel = root / "CDEL"
|
||||
failures: list[str] = []
|
||||
|
||||
if (panel / "TOP.BIN").exists():
|
||||
failures.append("завершённое удаление TOP.BIN ошибочно откатилось")
|
||||
|
||||
keep = panel / "KEEP.TXT"
|
||||
if not keep.is_file() or keep.read_bytes() != b"unselected cancel sentinel\r\n":
|
||||
failures.append("невыбранный KEEP.TXT отсутствует или изменён")
|
||||
|
||||
tree = panel / "TREE"
|
||||
if not tree.is_dir():
|
||||
failures.append("TREE успел удалиться целиком: runtime-cancel не подтверждён")
|
||||
else:
|
||||
remaining = list(tree.glob("D*/ITEM.BIN"))
|
||||
if not remaining:
|
||||
failures.append("после cancel не осталось ни одного файла дерева")
|
||||
for path in remaining:
|
||||
index = int(path.parent.name[1:])
|
||||
if path.read_bytes() != payload(index):
|
||||
failures.append(f"повреждён оставшийся файл: {path.name}")
|
||||
break
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: runtime cancel kept remaining tree; completed TOP delete remains.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт и проверяет HDD-fixture рекурсивного группового F8."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(size: int, seed: int) -> bytes:
|
||||
return bytes(((index * 29 + seed) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
panel = root / "RDEL"
|
||||
adir = panel / "ADIR"
|
||||
bdir = panel / "BDIR"
|
||||
keepdir = panel / "KEEPDIR"
|
||||
|
||||
(adir / "EMPTY").mkdir(parents=True)
|
||||
bdir.mkdir(parents=True)
|
||||
keepdir.mkdir(parents=True)
|
||||
|
||||
# Несколько файлов в одном каталоге проверяют, что F_NEXT продолжает
|
||||
# перечисление после удаления только что возвращённой записи.
|
||||
for index, size in enumerate((0, 1, 16, 4097)):
|
||||
(adir / f"A{index}.BIN").write_bytes(payload(size, index + 3))
|
||||
|
||||
deep = adir
|
||||
for depth in range(1, 13):
|
||||
deep = deep / f"D{depth:02d}"
|
||||
deep.mkdir()
|
||||
(deep / f"F{depth:02d}.TXT").write_bytes(
|
||||
payload(depth * 7, depth + 19)
|
||||
)
|
||||
|
||||
(bdir / "B0.TXT").write_bytes(b"delete second root\r\n")
|
||||
(bdir / "B1.BIN").write_bytes(payload(513, 71))
|
||||
(panel / "TOP.BIN").write_bytes(payload(33, 91))
|
||||
|
||||
(panel / "KEEP.TXT").write_bytes(b"keep top-level payload\r\n")
|
||||
(keepdir / "SAFE.BIN").write_bytes(payload(257, 111))
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
panel = root / "RDEL"
|
||||
failures: list[str] = []
|
||||
|
||||
for name in ("ADIR", "BDIR", "TOP.BIN"):
|
||||
if (panel / name).exists():
|
||||
failures.append(f"выбранный объект не удалён: {name}")
|
||||
|
||||
keep_file = panel / "KEEP.TXT"
|
||||
if not keep_file.is_file() or keep_file.read_bytes() != b"keep top-level payload\r\n":
|
||||
failures.append("невыбранный KEEP.TXT отсутствует или изменён")
|
||||
|
||||
safe = panel / "KEEPDIR" / "SAFE.BIN"
|
||||
if not safe.is_file() or safe.read_bytes() != payload(257, 111):
|
||||
failures.append("невыбранное дерево KEEPDIR изменено")
|
||||
|
||||
remaining = sorted(
|
||||
path.relative_to(panel).as_posix().upper()
|
||||
for path in panel.rglob("*")
|
||||
)
|
||||
if remaining != ["KEEP.TXT", "KEEPDIR", "KEEPDIR/SAFE.BIN"]:
|
||||
failures.append(f"неожиданный остаток RDEL: {remaining}")
|
||||
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: selected file and two directory trees deleted; KEEP preserved.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Создаёт payload и проверяет точные FAT16-метаданные после F5."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pathlib
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
EXPECTED_ATTR = 0x27 # readonly + hidden + system + archive
|
||||
EXPECTED_DATE = ((2001 - 1980) << 9) | (2 << 5) | 3
|
||||
EXPECTED_TIME = (4 << 11) | (5 << 5) | (6 // 2)
|
||||
PAYLOAD = bytes(((index * 43 + 17) & 0xFF) for index in range(4097))
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
source = root / "MSRC"
|
||||
target = root / "MDST"
|
||||
source.mkdir(parents=True)
|
||||
target.mkdir(parents=True)
|
||||
path = source / "META.BIN"
|
||||
path.write_bytes(PAYLOAD)
|
||||
timestamp = datetime.datetime(2001, 2, 3, 4, 5, 6).timestamp()
|
||||
os.utime(path, (timestamp, timestamp))
|
||||
|
||||
|
||||
class Fat16:
|
||||
def __init__(self, raw: bytes) -> None:
|
||||
self.raw = raw
|
||||
part = struct.unpack_from("<I", raw, 446 + 8)[0]
|
||||
self.part = part * 512
|
||||
self.bps = struct.unpack_from("<H", raw, self.part + 11)[0]
|
||||
self.spc = raw[self.part + 13]
|
||||
reserved = struct.unpack_from("<H", raw, self.part + 14)[0]
|
||||
fats = raw[self.part + 16]
|
||||
self.root_entries = struct.unpack_from("<H", raw, self.part + 17)[0]
|
||||
self.spf = struct.unpack_from("<H", raw, self.part + 22)[0]
|
||||
self.fat = self.part + reserved * self.bps
|
||||
self.root = self.fat + fats * self.spf * self.bps
|
||||
root_sectors = (self.root_entries * 32 + self.bps - 1) // self.bps
|
||||
self.data = self.root + root_sectors * self.bps
|
||||
self.cluster_size = self.bps * self.spc
|
||||
|
||||
def cluster_offset(self, cluster: int) -> int:
|
||||
return self.data + (cluster - 2) * self.cluster_size
|
||||
|
||||
def next_cluster(self, cluster: int) -> int:
|
||||
return struct.unpack_from("<H", self.raw, self.fat + cluster * 2)[0]
|
||||
|
||||
def directory_bytes(self, cluster: int | None) -> bytes:
|
||||
if cluster is None:
|
||||
return self.raw[self.root:self.root + self.root_entries * 32]
|
||||
result = bytearray()
|
||||
while 2 <= cluster < 0xFFF8:
|
||||
offset = self.cluster_offset(cluster)
|
||||
result += self.raw[offset:offset + self.cluster_size]
|
||||
cluster = self.next_cluster(cluster)
|
||||
return bytes(result)
|
||||
|
||||
@staticmethod
|
||||
def short_name(name: str) -> bytes:
|
||||
stem, dot, ext = name.upper().partition(".")
|
||||
if len(stem) > 8 or len(ext) > 3:
|
||||
raise ValueError(f"not an 8.3 name: {name}")
|
||||
return stem.ljust(8).encode("ascii") + ext.ljust(3).encode("ascii")
|
||||
|
||||
def find(self, directory: int | None, name: str) -> bytes | None:
|
||||
wanted = self.short_name(name)
|
||||
data = self.directory_bytes(directory)
|
||||
for offset in range(0, len(data), 32):
|
||||
entry = data[offset:offset + 32]
|
||||
if entry[0] == 0x00:
|
||||
break
|
||||
if entry[0] == 0xE5 or entry[11] == 0x0F:
|
||||
continue
|
||||
if entry[:11] == wanted:
|
||||
return entry
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cluster(entry: bytes) -> int:
|
||||
return struct.unpack_from("<H", entry, 26)[0]
|
||||
|
||||
def file_bytes(self, entry: bytes) -> bytes:
|
||||
remaining = struct.unpack_from("<I", entry, 28)[0]
|
||||
cluster = self.cluster(entry)
|
||||
result = bytearray()
|
||||
while remaining and 2 <= cluster < 0xFFF8:
|
||||
offset = self.cluster_offset(cluster)
|
||||
take = min(remaining, self.cluster_size)
|
||||
result += self.raw[offset:offset + take]
|
||||
remaining -= take
|
||||
cluster = self.next_cluster(cluster)
|
||||
return bytes(result)
|
||||
|
||||
|
||||
def metadata(entry: bytes) -> tuple[int, int, int]:
|
||||
return (
|
||||
entry[11],
|
||||
struct.unpack_from("<H", entry, 24)[0],
|
||||
struct.unpack_from("<H", entry, 22)[0],
|
||||
)
|
||||
|
||||
|
||||
def records(raw_path: pathlib.Path) -> tuple[Fat16, bytes, bytes | None]:
|
||||
fat = Fat16(raw_path.read_bytes())
|
||||
source_dir = fat.find(None, "MSRC")
|
||||
target_dir = fat.find(None, "MDST")
|
||||
if source_dir is None or target_dir is None:
|
||||
raise RuntimeError("MSRC/MDST отсутствуют в root FAT")
|
||||
source = fat.find(fat.cluster(source_dir), "META.BIN")
|
||||
target = fat.find(fat.cluster(target_dir), "META.BIN")
|
||||
if source is None:
|
||||
raise RuntimeError("MSRC/META.BIN отсутствует")
|
||||
return fat, source, target
|
||||
|
||||
|
||||
def verify_source(raw_path: pathlib.Path) -> None:
|
||||
fat, source, target = records(raw_path)
|
||||
failures: list[str] = []
|
||||
if target is not None:
|
||||
failures.append("MDST не пуст до MAME")
|
||||
if metadata(source) != (EXPECTED_ATTR, EXPECTED_DATE, EXPECTED_TIME):
|
||||
failures.append(f"неверные source metadata: {metadata(source)!r}")
|
||||
if fat.file_bytes(source) != PAYLOAD:
|
||||
failures.append("source payload повреждён")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: source FAT metadata prepared exactly.")
|
||||
|
||||
|
||||
def verify(raw_path: pathlib.Path) -> None:
|
||||
fat, source, target = records(raw_path)
|
||||
failures: list[str] = []
|
||||
if target is None:
|
||||
failures.append("MDST/META.BIN не создан")
|
||||
else:
|
||||
if metadata(target) != metadata(source):
|
||||
failures.append(
|
||||
f"metadata differ: src={metadata(source)!r} dst={metadata(target)!r}"
|
||||
)
|
||||
if fat.file_bytes(target) != PAYLOAD:
|
||||
failures.append("target payload не совпадает")
|
||||
if metadata(source) != (EXPECTED_ATTR, EXPECTED_DATE, EXPECTED_TIME):
|
||||
failures.append("source metadata изменены")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: F5 preserved exact FAT date/time and R/H/S/A attributes.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {
|
||||
"create", "verify-source", "verify"
|
||||
}:
|
||||
raise SystemExit(
|
||||
f"usage: {sys.argv[0]} create|verify-source|verify PATH"
|
||||
)
|
||||
path = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(path)
|
||||
elif sys.argv[1] == "verify-source":
|
||||
verify_source(path)
|
||||
else:
|
||||
verify(path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fixture и побайтная проверка транзакционного overwrite P19."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(seed: int, size: int) -> bytes:
|
||||
return bytes(((index * 29 + seed) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
SOURCES = {
|
||||
"NORMAL.BIN": payload(11, 4097),
|
||||
"READONLY.BIN": payload(23, 8193),
|
||||
"ABSENT.BIN": payload(37, 17),
|
||||
"COLLIDE.BIN": payload(51, 4096),
|
||||
"ROLLBACK.BIN": payload(67, 4096),
|
||||
}
|
||||
OLD = {
|
||||
"NORMAL.BIN": b"old-normal-target",
|
||||
"READONLY.BIN": b"old-readonly-target",
|
||||
"COLLIDE.BIN": b"old-collision-target",
|
||||
"ROLLBACK.BIN": b"old-rollback-must-survive",
|
||||
}
|
||||
COLLISION = b"pre-existing backup name must survive"
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
source = root / "SOURCE"
|
||||
target = root / "TARGET"
|
||||
source.mkdir(parents=True)
|
||||
target.mkdir(parents=True)
|
||||
for name, data in SOURCES.items():
|
||||
(source / name).write_bytes(data)
|
||||
for name, data in OLD.items():
|
||||
(target / name).write_bytes(data)
|
||||
(target / "~SB0000.BAK").write_bytes(COLLISION)
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
target = root / "TARGET"
|
||||
expected = {
|
||||
"NORMAL.BIN": SOURCES["NORMAL.BIN"],
|
||||
"READONLY.BIN": SOURCES["READONLY.BIN"],
|
||||
"COLLIDE.BIN": SOURCES["COLLIDE.BIN"],
|
||||
"ABSENT.BIN": SOURCES["ABSENT.BIN"],
|
||||
"RENAMED.BIN": SOURCES["ABSENT.BIN"],
|
||||
"ROLLBACK.BIN": OLD["ROLLBACK.BIN"],
|
||||
"~SB0000.BAK": COLLISION,
|
||||
}
|
||||
failures: list[str] = []
|
||||
for name, data in expected.items():
|
||||
path = target / name
|
||||
if not path.is_file() or path.read_bytes() != data:
|
||||
failures.append(f"неверный target {name}")
|
||||
unexpected = sorted(
|
||||
path.name for path in target.glob("~SB*.BAK")
|
||||
if path.name.upper() != "~SB0000.BAK"
|
||||
)
|
||||
if unexpected:
|
||||
failures.append(f"оставлены служебные backup: {unexpected!r}")
|
||||
result = root / "RESULT.TXT"
|
||||
text = result.read_text("ascii", errors="replace") if result.exists() else ""
|
||||
if "ALL PASS" not in text:
|
||||
failures.append("target-probe не записал ALL PASS")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: transactional overwrite, readonly, collision and rollback.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify PATH")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* p19_overwrite_probe.c — target-проверка backup/commit/rollback copy-job.
|
||||
*
|
||||
* UI-policy проверяется отдельно. Здесь ошибки подаются точно между
|
||||
* последним write и commit, чтобы доказать сохранность старого target.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <conio.h>
|
||||
#include <sprinter_mem.h>
|
||||
#include "sc_copy.h"
|
||||
|
||||
static ScCopyJob job;
|
||||
static int result_fd = -1;
|
||||
static uint8_t failures;
|
||||
|
||||
static void log_text(const char *text)
|
||||
{
|
||||
const char *end = text;
|
||||
uint16_t length;
|
||||
|
||||
cputs(text);
|
||||
while (*end != '\0') end++;
|
||||
length = (uint16_t)(end - text);
|
||||
if (result_fd >= 0) (void)write(result_fd, text, length);
|
||||
}
|
||||
|
||||
static void check(uint8_t condition, const char *name)
|
||||
{
|
||||
log_text(condition ? "PASS " : "FAIL ");
|
||||
if (!condition) failures++;
|
||||
log_text(name);
|
||||
log_text("\r\n");
|
||||
}
|
||||
|
||||
static int copy_file(const char *source_name, const char *target_name,
|
||||
uint32_t size, uint8_t page, uint8_t replace)
|
||||
{
|
||||
int step;
|
||||
|
||||
if (sc_copy_begin_policy(&job, "D:\\SOURCE", "D:\\TARGET",
|
||||
source_name, target_name, size, page,
|
||||
replace) != 0) return SC_COPY_STEP_ERROR;
|
||||
do {
|
||||
step = sc_copy_step(&job);
|
||||
} while (step == SC_COPY_STEP_MORE);
|
||||
return step;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint16_t total_before = 0u;
|
||||
uint16_t free_before = 0u;
|
||||
uint16_t total_after = 0u;
|
||||
uint16_t free_after = 0u;
|
||||
uint8_t block;
|
||||
uint8_t page;
|
||||
int step;
|
||||
|
||||
textattr(0x07u);
|
||||
clrscr_attr(0x07u);
|
||||
cputs("P19 transactional overwrite\r\n\r\n");
|
||||
result_fd = open("D:\\RESULT.TXT", O_WRONLY | O_CREAT | O_TRUNC);
|
||||
mem_info(&total_before, &free_before);
|
||||
block = mem_alloc_pages(1u);
|
||||
if (block == 0u) return 1;
|
||||
page = mem_get_page(block, 0u);
|
||||
|
||||
errno = 0;
|
||||
step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET",
|
||||
"NORMAL.BIN", 4097ul, page);
|
||||
check(step != 0 && job.error == EEXIST,
|
||||
"default still refuses existing target");
|
||||
|
||||
step = copy_file("NORMAL.BIN", "NORMAL.BIN", 4097ul, page, 1u);
|
||||
check(step == SC_COPY_STEP_DONE && job.committed,
|
||||
"overwrite commits through backup");
|
||||
|
||||
step = copy_file("READONLY.BIN", "READONLY.BIN", 8193ul, page, 1u);
|
||||
check(step == SC_COPY_STEP_DONE && job.committed &&
|
||||
(job.existing_attr & 1u), "explicit readonly overwrite");
|
||||
|
||||
step = copy_file("COLLIDE.BIN", "COLLIDE.BIN", 4096ul, page, 1u);
|
||||
check(step == SC_COPY_STEP_DONE && job.committed,
|
||||
"pre-existing backup name skipped");
|
||||
|
||||
step = copy_file("ABSENT.BIN", "ABSENT.BIN", 17ul, page, 1u);
|
||||
check(step == SC_COPY_STEP_DONE && job.committed,
|
||||
"replace policy accepts vanished target");
|
||||
|
||||
step = copy_file("ABSENT.BIN", "RENAMED.BIN", 17ul, page, 0u);
|
||||
check(step == SC_COPY_STEP_DONE && job.committed,
|
||||
"distinct destination name");
|
||||
|
||||
step = sc_copy_begin_policy(&job, "D:\\SOURCE", "D:\\TARGET",
|
||||
"ROLLBACK.BIN", "ROLLBACK.BIN",
|
||||
4096ul, page, 1u);
|
||||
if (step == 0) step = sc_copy_step(&job);
|
||||
if (step == SC_COPY_STEP_MORE) {
|
||||
(void)close(job.temp_fd);
|
||||
job.temp_fd = -1;
|
||||
(void)unlink(job.temp_path);
|
||||
step = sc_copy_step(&job);
|
||||
}
|
||||
check(step == SC_COPY_STEP_ERROR && !job.committed,
|
||||
"rename failure rolls original target back");
|
||||
|
||||
mem_free_block(block);
|
||||
mem_info(&total_after, &free_after);
|
||||
check(total_before == total_after && free_before == free_after,
|
||||
"EMM restored");
|
||||
if (failures == 0u) log_text("ALL PASS\r\n");
|
||||
if (result_fd >= 0) (void)close(result_fd);
|
||||
cputs("\r\nPress Enter to return to DSS.\r\n");
|
||||
while (kbhit()) (void)getkey();
|
||||
(void)getkey();
|
||||
return failures;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fixture и проверка UI-policy overwrite/skip/rename/cancel P19."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
def payload(seed: int, size: int) -> bytes:
|
||||
return bytes(((index * 31 + seed) & 0xFF) for index in range(size))
|
||||
|
||||
|
||||
SOURCES = {
|
||||
"AONE.BIN": payload(1, 4097),
|
||||
"ATWO.BIN": payload(2, 8193),
|
||||
"CANCEL.BIN": payload(3, 33),
|
||||
"REN.BIN": payload(4, 257),
|
||||
"RONLY.BIN": payload(5, 5000),
|
||||
"SONE.BIN": payload(6, 65),
|
||||
"STWO.BIN": payload(7, 66),
|
||||
}
|
||||
OLD = {name: ("old-" + name).encode("ascii") for name in SOURCES}
|
||||
|
||||
|
||||
def create(root: pathlib.Path) -> None:
|
||||
source = root / "P19SRC"
|
||||
target = root / "P19DST"
|
||||
source.mkdir(parents=True)
|
||||
target.mkdir(parents=True)
|
||||
for name, data in SOURCES.items():
|
||||
(source / name).write_bytes(data)
|
||||
for name, data in OLD.items():
|
||||
(target / name).write_bytes(data)
|
||||
|
||||
|
||||
def verify(root: pathlib.Path) -> None:
|
||||
target = root / "P19DST"
|
||||
expected = {
|
||||
"AONE.BIN": SOURCES["AONE.BIN"],
|
||||
"ATWO.BIN": SOURCES["ATWO.BIN"],
|
||||
"RONLY.BIN": SOURCES["RONLY.BIN"],
|
||||
"SONE.BIN": OLD["SONE.BIN"],
|
||||
"STWO.BIN": OLD["STWO.BIN"],
|
||||
"REN.BIN": OLD["REN.BIN"],
|
||||
"CANCEL.BIN": OLD["CANCEL.BIN"],
|
||||
"NEXT.BIN": SOURCES["REN.BIN"],
|
||||
}
|
||||
failures: list[str] = []
|
||||
for name, data in expected.items():
|
||||
path = target / name
|
||||
if not path.is_file() or path.read_bytes() != data:
|
||||
failures.append(f"неверный target {name}")
|
||||
garbage = sorted(
|
||||
path.name for path in target.iterdir()
|
||||
if path.name.upper().startswith(("~SC", "~SB"))
|
||||
)
|
||||
if garbage:
|
||||
failures.append(f"оставлены служебные файлы: {garbage!r}")
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"FAIL: {failure}")
|
||||
raise SystemExit(1)
|
||||
print("PASS: overwrite-all, readonly prompt, skip-all, rename and cancel.")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}:
|
||||
raise SystemExit(f"usage: {sys.argv[0]} create|verify PATH")
|
||||
root = pathlib.Path(sys.argv[2])
|
||||
if sys.argv[1] == "create":
|
||||
create(root)
|
||||
else:
|
||||
verify(root)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,32 @@
|
||||
# P1 platform probe для Sprinter Commander.
|
||||
#
|
||||
# Собирает два файла:
|
||||
# p1probe.exe — интерактивные пробы каталогов, EMM, экрана, клавиатуры,
|
||||
# DSKINFO и EXEC;
|
||||
# p1child.exe — дочерняя программа для проверки EXEC/возврата.
|
||||
#
|
||||
# Пробы намеренно собираются в small: это изолированный диагностический
|
||||
# инструмент, а не topology будущего Commander. Сборка big проверяется в P2.
|
||||
|
||||
PROJ_ROOT := $(abspath $(CURDIR)/../../../..)
|
||||
EXAMPLE := p1probe
|
||||
MEMORY := small
|
||||
ALLOCS ?= 3000
|
||||
EXTRA_FLAGS := --safe --max-allocs $(ALLOCS)
|
||||
EXTRA_SRCS := p1_sys.c p1_emm.c p1_dir.c p1_screen.c p1_keyboard.c p1_exec_test.c
|
||||
EXTRA_DATA := p1child.exe
|
||||
|
||||
include $(PROJ_ROOT)/app.mk
|
||||
|
||||
# EXEC-тест ожидает дочерний EXE рядом с основной пробой.
|
||||
$(EXE): Makefile p1child.exe
|
||||
|
||||
p1child.exe: p1child.c
|
||||
$(SPRINTER_CC) --memory tiny --safe --max-allocs $(ALLOCS) -o $@ $<
|
||||
|
||||
clean-child:
|
||||
rm -f p1child.exe
|
||||
|
||||
clean: clean-child
|
||||
|
||||
.PHONY: clean-child
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user