Что сделано: 1. █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ GOTO для BAT Новый файл SHELL/Commands/GOTO.ASM ? содержит: cmd_goto ? обработчик команды: разбирает имя метки (с поддержкой GOTO :label и GOTO label, без учёта регистра), перематывает BAT-файл в начало (Dss.Move_FP, B=0), сбрасывает буфер чтения (MOVWORD.count=0) и включает режим поиска метки. BAT_CHECK_LABEL ? вызывается для каждой строки BAT-файла: пропускает строки-метки :label (без эха и выполнения, как в MS-DOS) и в режиме поиска пропускает все строки, пока не найдёт нужную метку. Изменённые файлы (правки внесены побайтово, кодировка CP866 и переносы строк сохранены): SHELL/SHELL.ASM ? include 'Commands/goto.asm' SHELL/BATCH.ASM ? DZ 'GOTO' : DW cmd_goto в BATLIST (команда только для BAT, как PAUSE/REM); вызов BAT_CHECK_LABEL в CMDMODE сразу после EVALCMD SHELL/Commands/BREAK.ASM ? при завершении BAT, если поиск метки не завершился, печатает ?Label not found? и прерывает выполнение (поведение MS-DOS) SHELL/Messages/main_txt.asm ? добавлено сообщение Label not found и алиас MAIN_MSG.LABEL_NOT_FOUND Как работает @echo off echo Start goto skip echo Это не выполнится :skip echo После метки GOTO skip ? файл перематывается в начало, строки пропускаются (без эха) до строки :skip, далее выполнение продолжается с echo После метки. Поддерживаются переходы вперёд и назад (циклы), без учёта регистра, поиск метки идёт от начала файла (первая совпавшая метка ? как в MS-DOS). Строки :метка сами по себе не выполняются и не эхотся. Если метки нет ? выводится Label not found и BAT-файл прерывается. ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ 2. █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ Теперь TAB остаётся в строке как символ 0x09, но трактуется как пробельный разделитель только при разборе на токены: MOVWORD (BATCH.ASM) ? TAB больше не завершает строку: байт сохраняется в буфер как есть, концом строки считаются только CR/LF (<0x20, кроме 0x09). Строки не разрываются по табуляции. CMDMODE, .A826C (BATCH.ASM) ? ведущие пробелы и табуляции обрезаются (отступы строк). CMDMODE, .A8276 (BATCH.ASM) ? граница ?команда / аргумент? теперь по пробелу или табуляции (вместо cpir по пробелу). Поэтому GOTO?:PASS1 распознаётся как команда GOTO с аргументом :PASS1. cmd_goto / BAT_CHECK_LABEL (Commands/GOTO.ASM) ? пропуск ведущих пробелов теперь включает TAB (после имени команды, после :), сравнение метки ? без учёта регистра. В результате: @echo test 1?1234?=- ? выводится test 1?1234?=- с настоящей табуляцией (0x09 передаётся в консоль через Dss.PChars). GOTO?:LABEL и GOTO :LABEL оба работают; строки-метки :label не выполняются и не эхотся; отсутствие метки ? Label not found и прерывание BAT. ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ 3. █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ GOSUB/RETURN для BAT Как реализовано GOSUB: запрашивает текущий указатель файла (Dss.Move_FP, B=1 ? ?tell?), вычисляет смещение начала следующей строки = указатель ? MOVWORD.count (непрочитанные байты в буфере), кладёт это 32-битное смещение в стек возвратов, затем выполняет поиск метки как GOTO. RETURN: снимает смещение со стека, позиционирует файл туда (Dss.Move_FP, B=0), сбрасывает буфер ? NEWLINE продолжает со строки после GOSUB. Стек возвратов: gosub_stk (16?4 байта) + gosub_sp, сбрасывается в cmd_break.exit при каждом завершении BAT. Метки и поиск ? общий механизм с GOTO (goto_active/goto_label/BAT_CHECK_LABEL), TAB трактуется как пробел при разборе (как и было сделано для GOTO), поэтому GOSUB?:LABEL тоже работает. Ошибки (сообщение + прерывание BAT): RETURN without GOSUB ? RETURN без активного GOSUB GOSUB nesting too deep ? переполнение стека (>16) Label not found ? метка GOSUB/GOTO не найдена Изменённые файлы SHELL/Commands/GOTO.ASM ? рефакторинг (общие GOTO_PARSE/GOTO_START) + cmd_gosub, cmd_return, GOSUB_PUSH, стек, gAbort. SHELL/BATCH.ASM ? в BATLIST добавлены GOSUB/RETURN. SHELL/Commands/BREAK.ASM ? сброс стека GOSUB при завершении BAT. SHELL/Messages/main_txt.asm ? сообщения RETURN without GOSUB, GOSUB nesting too deep. ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ 4. █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ IF для BAT Поддерживаемый синтаксис IF [NOT] ERRORLEVEL число команда IF [NOT] EXIST файл команда IF [NOT] строка1==строка2 команда Если условие истинно ? остаток строки выполняется как обычная команда (GOTO, GOSUB, ECHO, запуск программы, вложенный IF и т.д.). Семантика (как в MS-DOS) ERRORLEVEL n ? истина, если код завершения предыдущей программы >= n (через Dss.Get_Err #42). Число 0..255. EXIST файл ? истина, если файл/каталог существует (через Dss.F_First, атрибуты ?всё, кроме метки тома?). строка1==строка2 ? посимвольное сравнение с учётом регистра (как в COMMAND.COM). Разделитель ? ==. NOT ? инверсия условия. %VAR% / %1 уже подставлены EVALCMD до выполнения IF, поэтому работает классика IF "%1"=="" GOTO end. TAB трактуется как пробел (согласовано с ранее сделанным для GOTO). Как работает повторная диспетчеризация После проверки условия IF_RUN находит начало команды-остатка, вычисляет длину её токена и делает jp COMP.start с hl=BATLIST ? тот же путь, что и для обычной строки. Поэтому IF ? GOTO :label, IF ? GOSUB :sub, IF ? program.exe, и вложенные IF a==b IF c==d ? работают (через jp, без роста стека). Если условие ложно или команда пуста ? ret (ничего не делается). Изменённые файлы SHELL/Commands/IF.ASM ? новый: cmd_if, IF_KW (регистронезависимое распознавание ключевых слов), IF_ATOI8, IF_SKIPB, IF_RUN. SHELL/SHELL.ASM ? include 'Commands/if.asm'. SHELL/BATCH.ASM ? IF добавлен в BATLIST (batch-only, как GOTO/GOSUB). ████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████