1 
   2                 .CHARSET        1251
   3 
   4 ; Каллисто версия 0.9б                          ∗∗∗ БЕТА−ВЕРСИЯ ∗∗∗
   5 ; Авторское право (c) Васильев Илья Владимирович, г. Москва, 11 июня 2016 г.
   6 ; Каллисто это язык программирования для "Электроники МК−161", основанный на Форте и ЯМК.
   7 ; Документацию по Каллисто см. на вики http://pmk.the−hacker.ru/
   8 
   9 ; Каллисто поставляется под свободной лицензией, совместимой с GNU GPL v3
  10 ; Текст лицензии на русском языке размером в 56 строк находится в файле PRAVA.TXT
  11 ; Каллисто нельзя распространять без исходного текста и файла PRAVA.TXT
  12 ; Если вы несогласны с правами, которые предоставляет свободное программное обеспечение
  13 ; и обязанностями, которые оно налагает, удалите Каллисто со своих носителей.
  14 
  15 ; Минимальная версия компилятора: MK.EXE v1.25, MKL2MKP v0.27
  16 
  17 ; R0 временный регистр слова−примитива
  18 ; R1 временный регистр слова−примитива
  19 ; R2 RP, указатель целочисленного стека возвратов (RS) −− растёт вниз от 5090
  20 ; R3 S=SP, указатель десятичного стека (DS с плавающей запятой) −− растёт вниз от 998
  21 ;
  22 ; R4 временный регистр слова−примитива
  23 ; R5 временный регистр слова−примитива, может быть изменён NEXT, CALL, EXIT
  24 ; R6 RI указатель шитого кода, он же IP указатель инструкций, указывает на следующий CFA (когда R9==NEXTD)
  25 ;
  26 ; R7 W=WP, указатель слов Форта, содержит CFA исполняемого слова, слово−примитив может менять R7
  27 ; R8 JP указатель/регистр передачи управления NEXT, временный регистр слова−примитива
  28 ; R9 = NEXTP/NEXTD, адрес кода NEXT
  29 ; RA временный регистр, его может изменить BIOS
  30 ; RB временный регистр слова−примитива
  31 ; RC = RPUSHRIP/RPUSHRID, адрес кода сохранения указателя шитого кода RI в стеке возвратов
  32 ; RD   при загрузке/сохранении словаря содержит номер версии Каллисто
  33 ; RE = 256 (литерал)
  34 ;
  35 ; R15 RX Здесь сохраняется стек МК−161 при считывании регистра словом ИП
  36 ; R16 RY
  37 ; R17 RZ
  38 ; R18 RT
  39 ; R19 = 9007/9008 (DARK/LIGHT) номер регистра прокрутки для текущего цвета фона
  40 ;
  41 ; R9042 RI указатель шитого кода (когда R9==NEXTP)
  42 ; R9044 читает байт шитого кода, RI++
  43 ;
  44 ; Источники
  45 ; 1. Семёнов Ю.А. Программирование на языке Форт, 1991 (FORTH ИТЭФ Семёнова Ю.А., 1988)
  46 ; 2. Баранов С.Н., Ноздрунов Н.Р. Язык Форт и его реализации, 1988 (ФОРТ−ЕС Баранова С.Н., 1986)
  47 ; 3. Forth 200x Standardisation Committee. Forth Standard 2012, 10th November 2014
  48 ; 4. НПП "СЕМИКО". Организация работы с функциями, адресуемыми через регистры памяти.
  49 ;    НПКД.401348.001 Д1 изм. 9. Новосибирск, 2009
  50 ; 5. Стандарт FORTH−79
  51 ; 6. Журнал "BYTE" N8, 1980.
  52 ; 7. Стандарт FORTH−83
  53 ; 8. Стандарт FORTH ANSI 1994
  54 
  55 ; В Каллисто переменные хранятся в области двоичных регистров
  56 ; Двойная нумерация вызвана необходимостью обращения к переменным как из ЯМК, так и из Форта
  57 ; Третья нумерация p использована для экономии памяти и времени исполнения в поле параметров переменных USER
  58 ;
  59 
  60 rrXS0           .EQU      998                   ; 10998 Дно стека данных (DS)
  61 rrXR0           .EQU     5090                   ; 15092 Дно стека возвратов (RS)
  62 rrBUFBLK        .EQU     5094                   ; 15094 = 3af6 Номер блока в буфере и признак UPDATE
  63 rlBUFBLK        .EQU     5095
  64 rrDISKBUF       .EQU     5096                   ; 15096 = 3af8
  65 rrENDBUF        .EQU     8167                   ; 18167
  66 
  67 rRX             .EQU      15                    ; 10015
  68 rRY             .EQU      16                    ; 10016
  69 rRZ             .EQU      17                    ; 10017
  70 rRT             .EQU      18                    ; 10018
  71 
  72 rrXTIB          .EQU     1000                   ; 11000 Входной буфер терминала (94 байта) и 2 нулевых байта (адрес фиксирован)
  73 
  74 ; Область переменных USER проходит инициализацию при запуске Форта
  75 ; Изначально предназначена для многозадачности
  76 ; Эта часть переменных обнуляется при COLD
  77 
  78 rrSAVIN         .EQU     1096                   ; 11096  SAVIN − Сохранение >IN в INTERPRET для NUMBER и BASE для FL
  79 pSAVIN          .EQU       96
  80 rrBLK           .EQU     1098                   ; 11098  BLK − BLK=0, работа Каллисто с пульта (TIB)
  81 pBLK            .EQU       98                   ;              BLK!=0, работа с блоком номер BLK @
  82 rrIN            .EQU     1100                   ; 11100  >IN − Указатель смещения во входном (или экранном) буфере
  83 pIN             .EQU      100
  84 rrSCR           .EQU     1102                   ; 11102  SCR − Номер редактируемого экрана
  85 pSCR            .EQU      102
  86 rrCONTEXT       .EQU     1104                   ; 11104  CONTEXT − Указатель, с какого словаря следует начать просмотр при интерпретации
  87 pCONTEXT        .EQU      104
  88 rlCONTEXT       .EQU     1105
  89 rrCURRENT       .EQU     1106                   ; 11106  CURRENT − Указатель, к какому словарю будет отнесено новое слово
  90 pCURRENT        .EQU      106
  91 rlCURRENT       .EQU     1107
  92 rrSTATE         .EQU     1108                   ; 11108  STATE − STATE=0 − исполнение, STATE=128 − компиляция
  93 pSTATE          .EQU      108
  94 rlSTATE         .EQU     1109                   ;     
  95 rrBASE          .EQU     1110                   ; 11110  BASE − основание действующей системы счисления
  96 pBASE           .EQU      110
  97 rlBASE          .EQU     1111
  98 rrDPL           .EQU     1112                   ; 11112  DPL − позиция (десятичной) запятой в числе
  99 pDPL            .EQU      112
 100 rrCSP           .EQU     1114                   ; 11114  CSP − контрольное хранение значения указателя стека
 101 pCSP            .EQU      114
 102 rlCSP           .EQU     1115
 103 rrRNUM          .EQU     1116                   ; 11116  R# − позиция курсора при редактировании экрана или возникновении ошибки
 104 pRNUM           .EQU      116
 105 rrHLD           .EQU     1118                   ; 11118  HLD − Указатель позиции в выходном буфере, обычно PAD
 106 pHLD            .EQU      118
 107 rlHLD           .EQU     1119
 108 rrEXP           .EQU     1120                   ; 11120  EE − Десятичное значение порядка вводимого числа
 109 pEXP            .EQU      120
 110 rrERB           .EQU     1122                   ; 11122  ERB − Блокировка ухода в систему Форт при ERROR
 111 pERB            .EQU      122
 112 rrKbdMode       .EQU     1124                   ; 11124  Флаг клавиатуры 1: цифра / латинские / русские буквы = 0 / 1 / 2
 113 rrKbdCaps       .EQU     1125                   ; 11125  Флаг клавиатуры 2: строчные / заглавные = 0 / 1
 114 pKBDFLG         .EQU      124                   ; 11124  KBDFLG − Флаги клавиатуры
 115 
 116 rrCHWM1         .EQU     1125                   ; адрес rrCHW−1 для косвенной адресации с прединкриментом
 117 
 118 ; Эта часть переменных инициализируется в COLD
 119 ;
 120 nbEmpty         .EQU      126                   ; 126 сколько байт обнулит COLD перед инициализацией _FONT и далее
 121 
 122 ; Описание активного шрифта
 123 ; Порядок полей важен, они заполняются словом FONT!
 124 ;
 125 pFONT           .EQU      126                   ; 11126  _FONT − Структура терминала, описывающая активный шрифт
 126 rrCHW           .EQU     1126                   ; 11126  +0  максимальная ширина символа при выводе на индикатор
 127 rrCHH           .EQU     1127                   ; 11127  +1  высота символа при выводе на индикатор
 128 rrBSW           .EQU     1128                   ; 11128  +2  ширина курсора и среднего символа для BS
 129 rrSCRLN         .EQU     1129                   ; 11129  +3  на сколько линий поднять экран при прокрутке
 130 rrSCRLFIX       .EQU     1130                   ; 11130  +4  на сколько строк поднять курсор после прокрутки
 131 
 132 rrS0            .EQU     1131                   ; 11131  S0 − Указатель начала стека параметров
 133 rlS0            .EQU     1132
 134 pS0             .EQU      131
 135 rrR0            .EQU     1133                   ; 11133  R0 − Указатель начала стека возвратов
 136 rlR0            .EQU     1134
 137 pR0             .EQU      133
 138 rrDDP           .EQU     1135                   ; 11135  ДH − Указатель на первую свободную ячейку десятичного словаря
 139 rlDDP           .EQU     1136
 140 pDDP            .EQU      135
 141 rrDP            .EQU     1137                   ; 11137  H − Указатель на первую свободную ячейку словаря H @ = HERE
 142 pDP             .EQU      137
 143 rlDP            .EQU     1138
 144 rrVOCLINK       .EQU     1139                   ; 11139  VOC−LINK − Переменная связи наборов слов
 145 pVOCLINK        .EQU      139
 146 rrAUTOEXEC      .EQU     1141                   ; 11141  APP − код отсюда будет выполняться сразу после WARM
 147 pAUTOEXEC       .EQU      141
 148 rlAUTOEXEC      .EQU     1142
 149 
 150 rrDict          .EQU     1143                   ; 11143 bufDict: Отсюда начнётся словарь в байтовой области
 151 rrXVOC          .EQU     1159                   ; 11159 bufDict + 16
 152 ;frTASKM7       .EQU    11161                   ; 11161 Начало слова TASK в байтовой области, bufDict + 10018
 153 rrXDP           .EQU     1170                   ; 11170 Конец словаря в байтовой области, bufDict + 27
 154 
 155                 .ORG    0
 156                 GOTO    INIT                    ; Начало исполнения Каллисто по В/О С/П
 157 
 158 ;       ∗∗ Подпрограммы адресного интерпретатора ∗∗
 159 ;
 160 ; От скорости адресного интерпретатора зависит скорость всего языка, ради неё разрабатывают процессоры.
 161 ; Оптимизация приветствуется.  Скорость работы этих процедур очень важна.
 162 ;
 163 ; 1. 1 EE 4 быстрее, чем 4 F10^X
 164 ; 2. <−> быстрее, чем FR
 165 ; 3. PPRM 9044 быстрее, чем KRMD
 166 ; 4. Cx EE быстрее, чем 1 EE
 167 ;
 168 ; 1. 65536 vs RME FX^2
 169 ; 2. FX^2 vs ENT ∗
 170 ;
 171 ; Лучше всего скажется на быстродействии Каллисто реализация адресного интерпретатора ( NEXT CALL RETURN ),
 172 ; а также (FIND) и наиболее часто встречающихся длительных комбинаций на уровне машинного кода, то есть
 173 ; встроенной программы МК−161. Тогда даже возможна реализация современного механизма CREATE .. DOES>
 174 ; например, через механику модифицированного косвенного шитого кода.
 175 
 176 ; Следующие две подпрограммы вызваются косвенно через RC (КППС) из : и DOES>
 177 ; SETRIPRG и SETRIDAT внутри тела ":" содержат ссылки на RPUSHRIP и RPUSHRID
 178 ; В релизе их можно сделать двумя шагами
 179 
 180 ; Начало CALL при вызове из памяти программ.
 181 RPUSHRIP:
 182                 PPRM 9042                       ; RX := RI      ; Текущий указатель шитого кода
 183                 ENT RME / FANS <−> KINT M5 ∗ − KM2 RM5 KM2      ; RPUSH (RX)
 184                 RM7 RTN                         ; RX := W, оптимизация
 185 
 186 ;
 187 ; Начало CALL при вызове из памяти данных.
 188 RPUSHRID:
 189                 RM6 10001 +                     ; RX := RI      ; Текущий указатель шитого кода
 190                 ENT RME / FANS <−> KINT M5 ∗ − KM2 RM5 KM2      ; RPUSH (RX)
 191                 RM7 RTN                         ; RX := W, оптимизация
 192 
 193 ; Выяснить, надо ли очищать словари: COLD или WARM ?
 194 INIT:
 195                 2 PPM 9048                      ; Теперь С/П это обычная клавиша, не останавливает Каллисто
 196                 GSB SVER                        ; RX := номер версии Каллисто
 197                 RMD FX>=0 INIT1                 ; Отрицательное число в RД может означать возобновление работы после BYE
 198                 2 PPM 9010 FR +/−               ; Инициализация (очистка) графического экрана
 199 INIT1:          + PX=0 JCOLD                    ; Если RД не содержит правильный номер версии, COLD
 200                 MD                              ; RД := 0
 201                 .NUMT RWARM                     ; WARM −− слово высокого уровня, обращение к нему сложнее
 202                 PGOTO SETRIPRG                  ; Исполнение шитого кода с WARM
 203 
 204 JBYE:                                           ; Обработчик BYE
 205                 GSB SVER +/−                    ; Перезапуск произойдёт без очистки индикатора
 206                 MD                              ; Дать шанс INIT продолжить работу по WARM
 207                 R/S                             ; Выйти в режим автоматической работы калькулятора
 208                 KGOTO9                          ; Теоретически оператор может вернуться в Каллисто с помощью С/П
 209 
 210 JVERSION:       GSB SVER KM3                    ; Обработчик VERSION
 211 JNOP:           KGOTO9                          ; Обработчик NOP
 212 SVER:           ,9 RTN                          ; Коду ЯМК тоже иногда требуется проверять номер версии.
 213 
 214 ;−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
 215 ; BIOS: терминальный ввод/вывод на основе регистров функций МК−161
 216 ;−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
 217 
 218 ; Обычно код располагается в теле примитива, но мы хотим использовать 2−х байтовые ПП
 219 JCR:            PGSB CHPUTCR GSB CHPUTLF KGOTO9 ; Обработчик CR
 220 
 221 CHPUTLF:        PPRM9000 <−>                    ; Нам важна координата y курсора
 222                 PPRM rrCHH +                    ; Увеличиваем на высоту данного шрифта
 223                 65 FANS − − PX<0 CHPUTSCROLL    ; Прокрутка экрана, если не вмещаемся
 224                 FANS + <−> PPM9000 RTN          ; Обычный LF вниз на rrCHH строк
 225 CHPUTCR:        PPRM9000 Cx PPM9000 RTN         ; CR
 226 CHPUTSCROLL:    PPRM rrSCRLN PKM19              ; Прокрутить на то число линий, на которое данный шрифт прокручивается
 227                 PPRM 9000 <−>                   ; Позиция курсора по y
 228                 PPRM rrSCRLFIX −                ; Корректируем, ведь прокрутка у МК−161 слишком грубая
 229                 <−> PPM 9000 RTN                ; Устанавливаем курсор на новую позицию и выходим
 230 
 231 ;−−−−− ДРАЙВЕР ЭКРАНА −−−−−
 232 ; Пока вывод ограничен встроенными шрифтами, в будущем предусмотреть загружаемые шрифты, включая оба микро−шрифта
 233 ; Не меняет R0, но затрагивает стек МК−161 и RA
 234 ; Не делает КГРФ
 235 CHPUT:          MA
 236                 32 − FX>=0 CHPUT1               ; Управляющие символы разбираем отдельно
 237 CHPUT3:         PPRM9000                        ; Где находится курсор
 238                 129 PPRM rrCHW −                ; Предельная координата по горизонтали
 239                 − FX>=0 CHPUT2                  ; Грубая проверка, что символ уместится −− потом доработать
 240                 Cx PPM9000                      ; CR, по достижении конца строки
 241                 PGSB CHPUTLF                    ; LF
 242 CHPUT2:         RMA PPM9020 RTN                 ; Вывести символ
 243 CHPUT1:         19 + FX!=0 CHPUTCR              ; 13 −> CR      ; наверняка можно сократить
 244                 1 + PX!=0 CHPUTCLS              ; 12 −> CLS     ; с помощью FL1
 245                 1 + PX!=0 CHPUTHOME             ; 11 −> HOME
 246                 1 + PX!=0 CHPUTLF               ; 10 −> LF
 247                 1 + PX!=0 CHPUTTAB              ; 9 −> TAB
 248                 1 + PX!=0 CHPUTBS               ; 8 −> BS
 249                 1 + FX=0 CHPUT3                 ; 7 −> BELL
 250 CHPUTBELL:      440 ENT 10 PPM9052 RTN          ; Издать короткий звуковой сигнал
 251 CHPUTBS:        PPRM9000 PPRM rrBSW −           ; BS на заданное в описании шрифта количество точек назад
 252                 0 KMAX <−> FR                   ; Остановка у левой границы индикатора
 253                 PPM9000 RTN                     ; Установить курсор
 254 
 255 CHPUTCLS:       8 PKM19                         ; 8 линий при прокрутке означает очистку экрана
 256 CHPUTHOME:      Cx ENT PPM9000 RTN
 257 CHPUTTAB:       PPRM9000 32 + 224 KAND PPM9000 RTN
 258 
 259 JPAGE:          GSB CHPUTCLS  KGRPH  KGOTO9     ; Обработчик примитива PAGE
 260 
 261 ;−−−−− ДРАЙВЕР КЛАВИАТУРЫ −−−−−
 262 
 263 JKEY:           GSB CHGET  KM3 KGOTO9           ; Обработчик примитива KEY
 264 
 265 ;−−−−−−− Показать курсор, дождаться нажатия клавиши, убрать курсор
 266 CHGKEY:
 267                 GSB CSRON
 268 CHGKwait:       PPRM9029 MA
 269                 KNOT FX!=0 CHGKwait
 270 
 271 ;−−−−−− Показать/убрать курсор
 272 CSROFF:
 273 CSRON:          PPRM9001 +                      ; Сохраним атрибут в X1
 274                 3 PPM9001                       ; Установим атрибут XOR
 275                 PPRM rrCHH  PPRM rrBSW          ; Размер курсора для активного шрифта
 276                 PPM9013                         ; Вывод прямоугольника
 277                 KGRPH
 278                 FANS PPM9001                    ; Восстановим атрибут
 279 CHGETR1:        RTN
 280 
 281 ; Можно предусмотреть внешнюю клавиатуру −− через последовательный порт
 282 ; Также неплохо сделать курсор мигающим, отдельную форму для режима вставки
 283 ; Можно попробовать упростить, сделав линейный поиск при 0 в таблице.
 284 CHGET:          GSB CHGKEY
 285                 PPRM rrKbdMode
 286                 PX=0 CHGET7
 287                 .NUM tblKeyNum                  ; Клавиша нажата в цифровом режиме
 288                 RMA + KPRGM                     ; Считываем код символа из таблицы клавиатуры
 289                 FX=0 CHGETR2
 290                 RMA 20 − FX=0 CHGET01           ; F ? 
 291                 PGSB CHGKEY                     ; Получить код клавиши после F
 292                 .NUM tblKeyFNum
 293                 RMA + KPRGM
 294                 FX=0 CHGETR2
 295                 RMA 20 − FX!=0 JCHGET           ; F F ?
 296                 2 − FX=0 CHGET02                ; F P ?
 297 CHGETCAPS:      1 PPRM rrKbdCaps − PPM rrKbdCaps
 298 JCHGET:         PGOTO CHGET
 299 CHGET02:        9 − FX=0 JCHGET                 ; F РУС/ЛАТ ?
 300 CHGSETRUS:      2                               ; rrKbdMode := 2        ; русские буквы
 301 CHGSETMODE:     PPM rrKbdMode
 302                 GOTO JCHGET
 303 CHGET01:        11 − FX=0 JCHGET                ; РУС/ЛАТ ?
 304 CHGSETLAT:      1 GOTO CHGSETMODE               ; rrKbdMode := 1        ; английские буквы
 305 
 306 CHGET7:                                         ; Клавиша нажата в алфавитном режиме
 307                 Cx PGSB KbdToChar               ; Код символа без F
 308                 FX=0 CHGETR2                    ; Если он не нулевой, его и возвращаем
 309                 RMA 12 − FX=0 CHGET2NSP         ; Это ВП?
 310                 32                              ; ВП это пробел!
 311 CHGETR2:        RTN
 312 CHGET2NSP:      1 − FX=0 CHGET6CX
 313                 8 RTN                           ; CX это 8 (Backspace)
 314 CHGET6CX:       7 −                             ; F ?
 315                 PX=0 CHGET6                     ; Разобрать остальные клавиши
 316                 PGSB CHGKEY                     ; Ввести алфавитный символ после F
 317                 1 PGSB KbdToChar                ; Получить код символа от клавиши после F
 318                 FX=0 CHGETR2                    ; Если он не нулевой, его и возвращаем
 319                 RMA 12 − FX=0 CHGET11NTAB       ; Это F + ВП ?
 320                 9 RTN                           ; Значит, табуляция.
 321 CHGET11NTAB:    1 − FX=0 CHGET15CX
 322                 127 RTN                         ; F + CX это 127 (Delete)
 323 CHGET15CX:      9 − PX!=0 CHGETCAPS             ; F P ?
 324                 1 − FX=0 CHGET15a               ; ШГ−>
 325                 96 RTN                          ; '`'
 326 CHGET15a:       8 − PX!=0 CHGSETRUS             ; F РУС/ЛАТ ?
 327                 2 −
 328 JEQCHGET:       PX=0 CHGET
 329                 10 RTN                          ; F+ВВОД будет LF
 330 CHGET6:         2 −                             ; P ?
 331                 PX!=0 CHGSETMODE                ; Установка режима NUM
 332                 9 −                             ; РУС/ЛАТ ?
 333                 PX!=0 CHGSETLAT                 ; Если да, установим латинский режим
 334                 8 + FX=0 JEQCHGET               ; ШГ−>
 335                 95                              ; '_'
 336 CHGETR:         RTN
 337 
 338 ;−−−−−−−− Обращение к таблицам клавиатуры −−−−−−−−
 339 ; Вход:         RX = 0/1 означает обычное нажатие или с F
 340 ;               RA = код нажатой клавиши
 341 ; Выход:        RX = код символа или 0 (из таблицы)
 342 ;
 343 KbdToChar:      PPRM rrKbdCaps
 344                 −                               ; 0 строчные, иначе заглавные
 345                 PPRM rrKbdMode
 346                 1 −                             ; 0 лат, 1 рус
 347                 <−> FX!=0 KTClow
 348                 <−> FX=0 KTC1
 349                 .NUMT   tblKeyL
 350                 GOTO KTCfetch
 351 KTC1:           .NUMT   tblKeyR
 352                 GOTO KTCfetch
 353 KTClow:
 354                 <−> FX=0 KTC2
 355                 .NUMT   tblKeyLL
 356                 GOTO KTCfetch
 357 KTC2:           .NUMT   tblKeyRL
 358 KTCfetch:       RMA + KPRGM                     ; Считываем код символа из таблицы клавиатуры
 359                 RTN
 360 
 361 tblKeyNum:                                      ; Таблицы клавиатуры
 362                 .TEXT   "0123456789,. "
 363                 .DB     8,24
 364                 .TEXT   "+−"
 365                 .DB     179,176,183             ; ∗ / <−>
 366                 .TEXT   "\0?\0():"
 367                 .DB     59                      ; ";"
 368                 .TEXT   "@![]\0"
 369                 .DB     27,13
 370                 .DB     26,27,24,25             ; Клавиши выбора
 371 tblKeyFNum:     .DB     190                     ; 0
 372                 .TEXT   "'"                     ; 1
 373                 .DB     34                      ; 2 это "
 374                 .TEXT   "#$%^&∗∼"               ; 4 5 6 7 8 9
 375                 .DB     181                     ; ,
 376                 .TEXT   "\\"
 377                 .DB     9,127,192               ; ВП Cx B^
 378                 .DB     185,251,189             ; + − ∗
 379                 .TEXT   "/"                     ; /
 380                 .DB     191                     ; <−>
 381                 .TEXT   "\0?\0<="
 382                 .DB     177,178
 383                 .TEXT   ">|{}\0"
 384                 .DB     200,10                  ; Выход, Ввод
 385                 .DB     26,27,24,25             ; Клавиши выбора
 386 tblKeyR:
 387                 .DB     09dH,098H,099H,09aH     ; 0Э 1Ш 2Щ 3Ъ
 388                 .DB     093H,094H,095H,08eH     ; 4У 5Ф 6Х 7О
 389                 .DB     08fH,090H,09eH,09fH     ; 8П 9Р ,Ю /−/Я
 390                 .DB     0,0,09cH,096H           ; ВП" " СxBS B^Ь +Ц
 391                 .DB     091H,097H,092H,09bH     ; −С xЧ /Т <−>Ы
 392                 .DB     0,0f0H,0,86H            ; F KЁ P >Ж
 393                 .DB     087H,088H,089H,08aH     ; <З В/ОИ С/ПЙ ИПК
 394                 .DB     08bH,08cH,08dH,0        ; ПЛ БПМ ППН Р−ГРД−Г
 395                 .DB     084H,085H,080H,081H     ; Выход Ввод −>А <−Б
 396                 .DB     082H,083H               ; ^В _Г
 397 tblKeyRL:
 398                 .DB     0edH,0e8H,0e9H,0eaH     ; 0э 1ш 2щ 3ъ
 399                 .DB     0e3H,0e4H,0e5H,0aeH     ; 4у 5ф 6х 7о
 400                 .DB     0afH,0e0H,0eeH,0efH     ; 8п 9р ,ю /−/я
 401                 .DB     0,0,0ecH,0e6H           ; ВП" " СxBS B^ь +ц
 402                 .DB     0e1H,0e7H,0e2H,0ebH     ; −с xч /т <−>ы
 403                 .DB     0,0f1H,0,0a6H           ; F Kё P >ж
 404                 .DB     0a7H,0a8H,0a9H,0aaH     ; <з В/Ои С/Пй ИПк
 405                 .DB     0abH,0acH,0adH,0        ; Пй БПк ППл Р−ГРД−Г
 406                 .DB     0a4H,0a5H,0a0H,0a1H     ; Выход Ввод −>а <−б
 407                 .DB     0a2H,0a3H               ; ^в _г
 408 tblKeyL:
 409                 .TEXT   "XSTUNOPI"              ; 0X 1S 2T 3U 4N 5O 6P 7I
 410                 .TEXT   "JKYZ"                  ; 8J 9K ,Y /−/Z
 411                 .DB     0,0                     ; ВП Сx
 412                 .TEXT   "WQLRMV"                ; B^W +Q −L xR /M <−>V
 413                 .TEXT   "\0D\0\0"               ; F KD P >_
 414                 .TEXT   "ABCEFGH\0"             ; <A В/ОB С/ПC ИПE ПF БПG ППH Р−ГРД−Г
 415                 .DB     27,13,0c5H,0c4H         ; Выход Ввод −> <−
 416                 .DB     0c6H,0c7H               ; ^ _
 417 tblKeyLL:
 418                 .TEXT   "xstunopi"              ; 0x 1s 2t 3u 4n 5o 6p 7i
 419                 .TEXT   "jkyz"                  ; 8j 9k ,y /−/z
 420                 .DB     0,0                     ; ВП Сx
 421                 .TEXT   "wqlrmv"                ; B^w +q −l xr /m <−>v
 422                 .TEXT   "\0d\0\0"               ; F Kd P >`
 423                 .TEXT   "abcefgh\0"             ; <a В/Оb С/Пc ИПe Пf БПg ППh Р−ГРД−Г
 424                 .DB     27,13,0c5H,0c4H         ; Выход Ввод −> <−
 425                 .DB     0c6H,0c7H               ; ^ _
 426 
 427 ;#COLD
 428 ; COLD           ( −− )                               Холодная перезагрузка, со сбросом словарей.
 429 ;−−−−−−−−−−−−−−−−−−−−−−−−
 430 ; Первая словарная статья
 431 ; Слово COLD может использоваться, как более суровый вариант FORGET TASK
 432 ; На будущее −− вместо тупой очистки памяти можно самостоятельно подгрузить словарь Форта.
 433 LCOLD:          .DB     4                       ; NFA: 4 − длина имени
 434                 .TEXT   "COLD"                  ; К Имя слова
 435                 .DW     0                       ; LFA: Поле связи равно 0, завершающее слово набора FORTH
 436 COLD:           .DW     JCOLD                   ; CFA: Поле кода ссылается на поле параметров, слово на ЯМК
 437 JCOLD:                                          ; PFA: Поле параметров содержит программу на ЯМК
 438                 2 PPM 9010                      ; Инициализация графического экрана, шрифт 0
 439                 256 ME                          ; В RE всегда 256, экономит память и время
 440                 .NUMT   nbEmpty                 ; Столько байт предстоит обнулить
 441                 M0
 442                 999 M5                          ; Начиная с R1000, прединкримент
 443                 Cx
 444 COLD0:          KM5 FL0 COLD0                   ; Обнулим их все!
 445                 .NUMT   SSTR
 446                 PPM 9042                        ; Читаем из памяти программ
 447                 44 M0                           ; Всего 17+27 байт
 448 CLDCPY:         PPRM 9044 KM5 FL0 CLDCPY        ; Копируем область USER + заготовку словаря
 449                 PGOTO SETPRG                    ; Начать исполнять код из памяти программ
 450 
 451 SSTR:
 452 ; Теневая таблица для инициализации _FONT и шести переменных USER (5+12=17 байт)
 453 
 454                 .DB 8,8,6,1,0                   ; 11126..11130 rrCHW, rrCHH, rrBSW, rrSCRLN, rrSCRFIX для шрифта 0
 455 ;
 456 ;                   S0        R0        ДH       H        VOC−LINK   APP
 457 ;                   1131      1133      1135     1137       1139     1141 ∗
 458 ;               .DW rrXS0,    rrXR0,    10020,   rrXDP,   rrXVOC,    QUIT
 459 ;               .DW 10998,    15090,    10020,   11170,    11159,    QUIT ∗
 460                 .DB 2aH,0f6H, 3aH,0f2H, 27H,24H, 2bH,0a2H, 2bH,97H
 461                 .DW QUIT                ; APP
 462 
 463 ; Эти две статьи (27 байт) COLD переносит в словарь в области двоичных регистров
 464 ; Их поле данных сможет меняться
 465 ;
 466 ;#FORTH
 467 ; FORTH          ( −− )                               Сделать набор слов FORTH контекстным.
 468 ; LFORTH:
 469                 .DB     5                       ; 11143 = 2b87 = LFORTH:
 470                 .TEXT   "FORTH"                 ; 11144: F79
 471                 .DW     LLOADQ                  ; 11149:
 472                 .DW     SDOEP                   ; 11151 = 2b8f = FORTH: (xt)
 473                 .DW DOVOC                       ; 11153: Обработчик VOCABULARY для DOES>
 474                 .DB 1,20H                       ; 11155: псевдозаголовок первого слова
 475                 .DB 2bH,99H                     ; 11157: ссылка на LTASK в новой адресации
 476 
 477                                                 ; Этим адресом инициализируется VOC−LINK
 478                 .DW     0                       ; 11159 = 2b97 = XVOC: Это должно быть уже в байтовой памяти
 479 
 480 ;#TASK
 481 ; TASK           ( −− )                               Последнее слово ядра Каллисто.
 482 ; В Форте слово TASK чаще всего используется оператором FORGET для сброса словаря в начальное состояние.
 483 ;LTASK:
 484                 .DB     4                       ; 11161 = 2b99 = LTASK
 485                 .TEXT   "TASK"                  ; 11162:
 486                 .DB     2bH,87H                 ; 11166: 11143 rrDict, ссылка на LFORTH в единой адресации
 487                 .DW     JNOP                    ; 11168 = TASK: (xt)
 488                                                 ; 11170 = 2ba2: этим адресом инициализируется H
 489 
 490 ; Отсюда, из памяти программ, запускается шитый код −− сразу после "болванки" для инициализации USER и словаря.
 491 ;
 492                 .DW SPSTO,RPSTO                 ; Инициализируем оба стека
 493                 .DW DARK,PDOTQ                  ; Вывод названия и версии транслятора
 494                 .DB 22                          ; Длина первой строки
 495 strForthM1:     .DB 12                          ; Начнём с CLS
 496                 .TEXT "Каллисто версия 0.9б"    ; Эта фраза означает, что Каллисто удалила пользовательский словарь
 497                 .DB 10                          ; Закончим LF
 498                 .DW BRAN,RWARM                  ; Передаём управление WARM (оптимизация)
 499 
 500 ;#WARM
 501 ; WARM           ( −− )                               Тёплая перезагрузка, словарь сохраняется.
 502 ; Если может, запускает слово, токен которого лежит в APP
 503 LWARM:          .DB     4
 504                 .TEXT   "WARM"                  ; К
 505                 .DW     LCOLD
 506 WARM:           .DW CALL
 507 RWARM:          .DW SPSTO,RPSTO,DEC             ; Объём памяти выводится в десятичной системе
 508                 .DW ZERO,FONTSTO,DARK,LITB      ; Установка шрифта 0, вывод тёмным по светлому
 509                 .DB 13                          ; RC
 510                 .DW EMIT,FREE,DOT               ; Выводим размер свободной памяти
 511                 .DW PDOTQ
 512                 .DB 13                          ; Начнём с CR
 513                 .TEXT "байт свободно"           ; Суровое московское приветствие Каллисто
 514                 .DW BUFN,ZSTORE                 ; Сбрасываем флаг UPDATE и номер загруженного блока
 515                 .DW DISKOFF,LBRAC
 516                 .DB 2bH,8fH                     ; 11151 FORTH (xt)
 517                 .DW DEFIN, AUTOEXEC,UAT
 518                 .DW QDUP,ZBRAN,RQUIT            ; Или на ABORT?
 519                 .DW EXEC                        ; Начать исполнение стартового кода, обычно это QUIT
 520                 .DW BRAN,RQUIT                  ; На случай, если слово вернуло управление
 521 
 522 ;#BFREE
 523 ; BFREE          ( −− u )                             Оценить количество свободных байт.
 524 ; Положить на стек количество свободных байт в словаре. Слово уникально для Каллисто.
 525 LFREE:          .DB     5                       ; ( −− n) К
 526                 .TEXT   "BFREE"
 527                 .DW     LWARM
 528 FREE:           .DW CALL, RPAT,PAD,SUB, EXIT
 529 
 530 ;#BYE
 531 ; BYE            ( −− )                               Выход из Каллисто в калькулятор МК−161.
 532 ; Выход из системы может использоваться как временный, отладочный останов.
 533 LBYE:           .DB     3
 534                 .TEXT   "BYE"
 535                 .DW     LFREE
 536 BYE:            .DW     JBYE
 537 
 538 ;#VERSION
 539 ; VERSION        ( −− p)                              Версия Каллисто.
 540 ; Кладёт в стек номер версии Каллисто. Слово уникально для Каллисто.
 541 LVERSION:       .DB     7
 542                 .TEXT   "VERSION"               ; ( −− r) К
 543                 .DW     LBYE
 544 VERSION:        .DW     JVERSION
 545 
 546 ;#EXECUTE
 547 ; EXECUTE        ( т −− )                             Исполнить слово с токеном т (CFA).
 548 ; Исполняет слово, CFA которого хранится в стеке.
 549 LEXEC:          .DB     7
 550                 .TEXT   "EXECUTE"               ; ( i∗x xt −− j∗x) F79
 551                 .DW     LVERSION
 552 EXEC:           .DW     JEXEC
 553 
 554 ;#NOP
 555 ; НОП            ( −− )                               Нет операции.
 556 ; Пустая операция для инициализации DEFER −− ничего не делает.
 557 ; Название слова взято из К НОП (Б3−34).
 558 LNOP:           .DB     3,141,142,143           ; "НОП"
 559                 .DW     LEXEC
 560 SNOP:           .DW     JNOP
 561 
 562 ;#qBREAK
 563 ; ?BREAK         ( −− )                               Проверить клавиатуру на паузу и аварийную остановку.
 564 ; Проверяем клавиатуру на паузу и аварийную остановку.
 565 ; Возможно, код можно оптимизировать. Слово уникально для Каллисто.
 566 LQBREAK:        .DB     6
 567                 .TEXT   "?BREAK"                ; К
 568                 .DW     LNOP
 569 QBREAK:         .DW     JQBREAK
 570 JQBREAK:        PPRM 9028                       ; Клавиша нажата?
 571                 21 − KX=09                      ; Если нажата не К, продолжить
 572                 PGSB CSRON
 573 QBL:            PPRM 9028
 574                 KNOT FX=0 QBL
 575                 PPM 9029                        ; Очистим буфер клавиатуры
 576 QBL2:           PPRM 9029 MA                    ; Читаем код клавиши
 577                 KNOT FX!=0 QBL2
 578                 PGSB CSROFF
 579                 RMA 26 − KX=09                  ; Если не С/П, продолжаем
 580                 94 PPM 9020                     ; "^"
 581                 67 PPM 9020                     ; "C"
 582                 .NUM RABORT
 583                 PGOTO SETRIPRG                  ; Перейти на исполнение шитого кода с ABORT
 584 
 585 ;#xLITERAL
 586 ; (LITERAL)      ( −− x )                             Код периода выполнения для литерала.
 587 ; В шитом коде за (LITERAL) идёт двухбайтовое целое со знаком в шестнадцатеричном формате.
 588 LLIT:           .DB     9
 589                 .TEXT   "(LITERAL)"             ; Имя взято у Броуди, соответствует духу Каллисто
 590                 .DW     LQBREAK
 591 LITD:           .DW     JLITD
 592 JLITD:          KRM6 RME ∗ KRM6
 593 PLUSKM3M:       +                               ; Код повторно используется обработчиком CONSTANT
 594                 KM3                             ; PUSH MEMW[RI++]
 595                 32768 − KX>=09                  ; Если число положительное, NEXT
 596                 FANS − PKM03  KGOTO9            ; Обработка отрицательных чисел, NEXT
 597 LITP:           .DW     JLITP                   ; Литералы в области памяти программ
 598 JLITP:          PPRM9044 RME ∗ PPRM9044 +       ; Все литералы в области программ положительные!
 599                 KM3 KGOTO9                      ; PUSH MEMW[RI++]
 600 LITB:           .DW     JLITB
 601 JLITB:          PPRM9044 KM3 KGOTO9             ; Специальный байтовый литерал для памяти программ.
 602 
 603 ;#TYPE
 604 ; TYPE           ( a u −− )                           Напечатать на индикаторе u литер от адреса a.
 605 ; Передаёт u литер, начиная с адреса a на индикатор. В Форте вывод может дублироваться на принтер.
 606 ; Подразумеваем, что строка не пересекает границу областей памяти.
 607 LTYPE:          .DB     4
 608                 .TEXT   "TYPE"                  ; F79
 609                 .DW     LLIT
 610 TYPE:           .DW     JTYPE                   ; Примитив
 611 JTYPE:          RM3 M8 1 + MB 1 + M3
 612                 KRM8 M0 +/− KX<09               ; R0 := длина
 613                 KRMB MB 1 EE 4 − FX<0 TYPER
 614                 RMB
 615 TYPEP:          KPRGM PGSB CHPUT                ; вывести очередную литеру из памяти программ
 616                 RMB 1 + MB FL0 TYPEP
 617                 KGRPH KGOTO9                    ; NEXT
 618 TYPER:          1 − M5
 619 TYPERL:         KRM5 PGSB CHPUT                 ; вывести очередную литеру из регистровой памяти
 620                 FL0 TYPERL
 621                 KGRPH KGOTO9                    ; NEXT
 622 
 623 ;#TYPE1
 624 ; TYPE1          ( a u −− )                           Напечатать на индикаторе в одну строку u литер от адреса a.
 625 ; Вывести строку из памяти данных, заменяя управляющие коды и строго в одну строчку, до конца индикатора.
 626 LTYPE1:         .DB     5
 627                 .TEXT   "TYPE1"
 628                 .DW     LTYPE
 629 TYPE1:          .DW     JTYPE1                  ; Примитив
 630 JTYPE1:         9 EE 3 MA
 631                 RM3 M8 1 + MB 1 + M3
 632                 KRM8 M0 +/− KX<09               ; R0 := длина
 633                 KRMB 1 EE 4 − KX>=09
 634                 1 − M5
 635 TYPE1C:         KRMA MB
 636                 KRM5 PPM9020                    ; Вывести литеру
 637                 KRMA RMB − PX=0 TYPEC3          ; Курсор сдвинулся с места?
 638                 PPM9020                         ; Вывести '.' ( RX==0)
 639                 KRMA RMB − FX=0 TYPEC3          ; По−прежнему застряли?
 640                 Cx 127 MB KMA                   ; Возможно, RB уже 127, но мы перестрахуемся
 641                 Cx 7 + RMB PPM 9012             ; Вывод линии, признак продолжения строки
 642                 KGRPH KGOTO9
 643 TYPEC3:         PFL0 TYPE1C
 644                 KGRPH KGOTO9
 645 
 646 ;#qBRANCH
 647 ; ?BRANCH        ( ф −− )                             Условное ветвление. Переход в шитом коде, если ф=0.
 648 ; Служебное слово для реализации структур управления −− таких, как IF WHILE
 649 LZBRAN:         .DB     7
 650                 .TEXT   "?BRANCH"               ; ( f −− )
 651                 .DW     LTYPE1
 652 ZBRAND:         .DW     JZBRAND                 ; ?BRANCH в памяти данных
 653 JZBRAND:        RM3 M8 1 + M3 KRM8              ; POP RX
 654                 FX!=0 CNTD
 655                 KRM6 KRM6 KGOTO9                ; RI := RI+2, NEXT
 656 
 657 ZBRAN:          .DW     JZBRAN                  ; ?BRANCH в памяти программ
 658 JZBRAN:         RM3 M8 1 + M3 KRM8              ; POP RX
 659                 FX!=0 CNT                       ; если RX==0, переход
 660                 PPRM9044 PPRM9044               ; RI := RI+2, пропускаем ячейку
 661                 KGOTO9                          ; NEXT
 662 
 663 ;#BRANCH
 664 ; BRANCH         ( −− )                               Ветвление. Безусловный переход в шитом коде.
 665 ; Служебное слово для реализации структур управления −− таких, как ELSE BEGIN
 666 LBRAN:          .DB     6
 667                 .TEXT   "BRANCH"
 668                 .DW     LZBRAN
 669 BRAND:          .DW     CNTD                    ; Безусловный переход для памяти данных
 670 POPCNTD:        RM3 1 + M3
 671 CNTD:           KRM6 RME ∗ KRM6 + M6 KGOTO9
 672 
 673 BRAN:           .DW     CNT                     ; Безусловный переход для памяти программ
 674 POPCNT:         RM3 1 + M3
 675 CNT:
 676                 Cx PPM9210                      ; Чтение двухбайтового значения по номеру X=0
 677                 PPM9042                         ; RI := MEMW[RI]
 678                 KGOTO9                          ; NEXT
 679 
 680 ;#xFOR
 681 ; (FOR)          ( n −− )                             Начало цикла со счётчиком в шитом коде (слово без заголовка).
 682 ; Заголовок убран, слово (FOR) используется только словом FOR
 683 ; Можно убрать второе слово на стеке возвратов и сделать компиляцию LEAVE более интеллектуальной.
 684 ;LXFOR:         .DB     5
 685 ;               .TEXT   "(FOR)"
 686 ;               .DW     LBRAN
 687 XFORD:          .DW     JXFORD                  ; (FOR) в памяти данных
 688 JXFORD:         PKRM03 MA +/− PX<0 POPCNTD      ; Защита от n<=0
 689                 RM3 1 + M3                      ; POP n
 690                 KRM6 KRM6 KM2 <−> KM2           ; RPUSH MEMW[RI]        ; RI := RI+2
 691                 RMA ENT RME / KINT MA RME ∗ − KM2 RMA KM2               ; RPUSH n
 692                 KGOTO9                          ; NEXT
 693 
 694 XFOR:           .DW     JXFOR                   ; (FOR) в памяти программ
 695 JXFOR:          PKRM03 MA +/− PX<0 POPCNT
 696                 RM3 1 + M3                      ; POP n
 697                 PPRM9044 PPRM9044 KM2 <−> KM2   ; RPUSH MEMW[RI]        ; RI := RI+2
 698                 RMA ENT RME / KINT MA RME ∗ − KM2 RMA KM2               ; RPUSH n
 699                 KGOTO9                          ; NEXT
 700 
 701 ;#xNEXT
 702 ; (NEXT)         ( −− )                               Конец цикла со счётчиком в шитом коде (слово без заголовка).
 703 ; Заголовок убран, слово (NEXT) используется только словом NEXT
 704 ;LXNEXT:        .DB     6
 705 ;               .TEXT   "(NEXT)"
 706 ;               .DW     LXFOR
 707 XNEXTD:         .DW     JXNEXTD                 ; (NEXT) в памяти данных
 708 JXNEXTD:        RM2 MA 1 + MB
 709                 KRMB 1 − FX!=0 XNEQ0D           ; Переход если мл.байт был равен 1
 710                 FX<0 KMBCNTD
 711                 KRMA FX!=0 XNEXITD              ; Сюда перешли, если после декремента мл.байт < 0
 712                 1 − FX>=0 XNEXITD
 713                 KMA 255                         ; +++ Можно попробовать RME, вдруг само ограничит до 255
 714 KMBCNTD:        KMB PGOTO CNTD
 715 XNEQ0D:         KRMA FX!=0 XNEXITD              ; Переход, если счётчик == 0
 716                 Cx
 717                 GOTO KMBCNTD
 718 XNEXITD:        KRM6 KRM6                       ; Пропустить адрес перехода
 719 JUNLOOP:        4                               ; Обработчик UNLOOP
 720 NLEV:           RM2 + M2                        ; Убрать из стека возвратов два слова, счётчик и адрес
 721                 KGOTO9
 722 
 723 XNEXT:          .DW     JXNEXT                  ; (NEXT) в памяти программ
 724 JXNEXT:         RM2 MA 1 + MB
 725                 KRMB 1 − FX!=0 XNEQ0
 726                 FX<0 KMBCNT
 727                 KRMA FX!=0 XNEXIT
 728                 1 − FX>=0 XNEXIT
 729                 KMA 255                         ; +++ Можно попробовать RME, вдруг само ограничит до 255
 730 KMBCNT:         KMB PGOTO CNT
 731 XNEQ0:          KRMA FX!=0 XNEXIT
 732                 Cx
 733                 GOTO KMBCNT
 734 XNEXIT:         PPRM9044 PPRM9044               ; Пропустить адрес перехода
 735                 GOTO JUNLOOP
 736 JLEV:           2 GOTO NLEV                     ; Обработчик RDROP
 737 
 738 
 739 ;#xFIND
 740 ; (FIND)         ( a1 a2 −− a3 c 1 | 0 )              Поиск слова a1 в словаре a2. Если найдено, вернуть nfa и байт счётчика.
 741 ; Адрес_строки NFA => NFA длина TRUE/FALSE
 742 ; В отличии от Форта ИТЭФ мы возвращаем NFA
 743 ; a1 адрес строки со счётчиком −− слово, которое мы ищем (пока только в байтовой области)
 744 ; a2 NFA первого слова в начале списка слов, где мы ищем
 745 ; a3 CFA найденного слова в списке (может, всё−таки NFA?)
 746 ; c байт длины и флагов найденого в списке слова
 747 ; Сделать поиск и сравнение по словам
 748 ;
 749 ; Здесь в качестве истины возвращается 1, хотя Каллисто перешло на стандарт −1/0
 750 ; Возможно, слово нуждается в переделке.
 751 ;
 752 LPFIND:         .DB     6
 753                 .TEXT   "(FIND)"                ; ( a1 a2 −− a3 c 1 | 0 ) К
 754                 .DW     LBRAN
 755 PFIND:          .DW     JPFIND
 756 JPFIND:
 757 ; Сперва ищем в памяти данных
 758                 RM3 MA 1 + M3
 759                 PKRM03 1 EE 4 − MB              ; RB адрес слова, которое мы ищем (строка со счётчиком)
 760                 KRMB M0                         ; R0 Длина слова, которое мы ищем
 761                 RMB 1 + MB                      ; RB адрес первого символа слова, который мы ищем
 762                 KRMA
 763 DFAST:          M5 1 EE 4 − PX>=0 PFIND2
 764                 MA M5                           ; RA=M5 адрес NFA слова в списке слов, с которым сравниваем
 765                 KRMA PKM03 63 KAND              ; TOS байт длины и флагов очередного слова из списка
 766                 RM0 − FX=0 DSKIP                ; Если длины и флаг SMUDGE различны, пропустить слово
 767                 KRM5 KRMB − FX!=0 SLO           ; Если ещё и первые символы равны, сравнить слова подробней
 768 DSKIP:          PKRM03 31 KAND RMA + M5
 769                 KRM5 RME ∗ KRM5 + PX=0 DFAST    ; LFA, Смотреть след. слово
 770                 PKM03  KGOTO9                   ; Не нашли, в RX уже 0
 771 SLO:            RM0 M1  RMB M5  RMA 1 + M4  GOTO SLOIN ; Возможно можно обыграть R5 и сократить, ускорить поиск
 772 SLOL:           KRM4 KRM5 − FX=0 DSKIP
 773 SLOIN:          FL1 SLOL
 774                 PKRM03 RMA 1 EE 4 +
 775                 PGOTO PFTAIL                    ; NFA, нашли!
 776 
 777 ; Затем ищем в памяти программ
 778 PFIND2:         RM5
 779 FAST:           MA                              ; RA адрес NFA слова в списке слов, с которым сравниваем
 780                 KPRGM PKM03 63 KAND             ; TOS байт длины и флагов очередного слова из списка
 781                 RM0 − FX=0 SKIP                 ; Если длины и флаг SMUDGE различны, пропустить слово
 782                 RMA 1 + KPRGM KRMB − PX!=0 SLOW ; Если ещё и первые символы равны, сравнить слова подробней
 783 SKIP:           PKRM03 31 KAND 1 + RMA +
 784                 KPRGM FANS 1 + KPRGM
 785                 <−> RME ∗ + FX=0 FAST           ; LFA, Смотреть след. слово
 786                 PKM03  KGOTO9                   ; Не нашли, в RX уже 0
 787 SLOW:           RM0 M1  RMB M5  RMA 1 +  GOTO SLOWIN
 788 SLOWL:          1 + M8                          ; R8 следующий символ
 789                 KPRGM KRM5 − PX=0 SKIP
 790                 RM8
 791 SLOWIN:         FL1 SLOWL
 792                 PKRM03 RMA 
 793 PFTAIL:         PKM03                           ; NFA, нашли!
 794                 <−> KM3                         ; c
 795                 1 KM3 KGOTO9                    ; 1
 796 
 797 ;#DIGIT
 798 ; DIGIT          ( c u1 −− u2 1 | 0 )                 Цифра. Преобразовать литеру c в число u2, используя основание u1.
 799 ; ASCII−DIGIT BASE => DIGIT−VALUE TRUE / FALSE
 800 ; Признаёт только заглавные буквы
 801 ; Здесь в качестве истины возвращается 1, хотя Каллисто перешло на стандарт −1/0
 802 ; Возможно, слово нуждается в переделке.
 803 ;
 804 LDIGIT:         .DB     5
 805                 .TEXT   "DIGIT"                 ; ( литера основание −− значение 1 | 0 ) Форт ИТЭФ
 806                 .DW     LPFIND
 807 DIGIT:          .DW     JDIGIT
 808 
 809 ;#pe
 810 ; П              ( t z y x u −− t z y )               Запись в регистр МК−161 с номером u.
 811 ; Запись в регистр МК−161 Ru при стеке МК−161, заполненном на основе стека Форта.
 812 ; Не осуществляет преобразование адресов, полностью "сырое" обращение к регистру.
 813 ; Слово уникально для Каллисто.
 814 LRSTO:          .DB     1,143                   ; "П"
 815                 .DW     LDIGIT                  ; ( r1 r2 r3 значение номер −− r1 r2 r3 ) К
 816 RSTO:           .DW     JRSTO
 817 JRSTO:          PKRM03 MA RM3 2 + M3            ; RA := u
 818                 3 + M0
 819                 KRM0 KRM0 KRM0 KRM0
 820                 KMA KGOTO9
 821 
 822 ;#ipe
 823 ; ИП             ( u −− x )                           Чтение регистра МК−161 с номером u.
 824 ; Чтение из регистра МК−161 Ru с сохранением стека МК−161 в переменных RX RY RZ RT
 825 ; Не осуществляет преобразование адресов, полностью "сырое" обращение к регистру.
 826 ; Слово уникально для Каллисто.
 827 LIP:            .DB     2,136,143               ; "ИП"
 828                 .DW     LRSTO                   ; ( номер −− содержимое) К
 829 IP:             .DW     JIP
 830 JIP:            CX ENT ENT
 831                 PKRM03 MA CX KRMA
 832                 PKM03
 833                 PM rRX FR  PM rRY FR  PM rRZ <−>  PM rRT
 834                 KGOTO9
 835 
 836 ;#xipe
 837 ; (ИП)           ( −− x )                             Чтение регистра МК−161, указанного в шитом коде.
 838 ; Чтение из регистра, номер которого зашит в шитом коде.
 839 ; Помогает в реализации литералов с плавающей запятой.
 840 ; Похожа на команду РР ИП (МК−152), но номер регистра в шестнадцатеричном формате
 841 ; и считывание происходит в стек данных Каллисто. Слово уникально для Каллисто.
 842 LXREG:          .DB     4
 843                 .TEXT   "("
 844                 .DB     136,143                 ; "(ИП)"
 845                 .TEXT   ")"                     ; ( −− n) К
 846                 .DW     LIP
 847 XREG:           .DW     JXREG
 848 JXREG:          KRM6 RME ∗ KRM6 +
 849                 MA KRMA KM3
 850                 KGOTO9
 851 
 852 ;#ipeerghe
 853 ; ИПРГ           ( u −− c )                           Чтение из памяти программ.
 854 ; Обёртка вокруг К ИПРГ (МК−152). Слово уникально для Каллисто.
 855 LIPRG:          .DB     4,136,143,144,131       ; "ИПРГ" К
 856                 .DW     LXREG
 857 IPRG:           .DW     JIPRG
 858 JIPRG:          PKRM03 KPRGM PKM03
 859                 KGOTO9
 860 
 861 JDIGIT:                                         ; Обработчик DIGIT
 862                 RM3 MA                          ; [RA] == BASE
 863                 1 + M3
 864                 PKRM03 48 − FX>=0 STF           ; Правильная цифра = ASCII − 48
 865                 MB 10 − FX>=0 MO9               ; Если >9
 866                 3 + MB
 867                 10 − FX>=0 STF
 868 MO9:            RMB KRMA − FX<0 STF             ; Если не меньше BASE, то ошибка
 869                 RMB PKM03                       ; Запись цифры в стек, "успешный" выход
 870 JONE:                                           ; Обработчик 1
 871                 1 KM3 KGOTO9
 872 
 873 ;               ∗∗ Стандартные слова ∗∗
 874 ;               ∗∗ Условные операторы ∗∗
 875 ;
 876 ;#ZeroEqual
 877 ; 0=             ( x −− f )                           Проверка на равенство нулю.
 878 ; Слово 0= часто используется, как логическое отрицание NOT. Заменяет 0 на −1 и наоборот.
 879 LZEQU:          .DB     2
 880                 .TEXT   "0="                    ; ( x −− флаг )
 881                 .DW     LIPRG
 882 ZEQU:           .DW     JZEQU
 883 JZEQU:          PKRM03  FX!=0 STT
 884 STF:            Cx PKM03 KGOTO9                 ; [SP] := 0; NEXT
 885 
 886 ;#ZeroMore
 887 ; 0>             ( x −− f )                           Проверка на положительность.
 888 LZGRET:         .DB     2
 889                 .TEXT   "0>"                    ; ( x −− флаг )
 890                 .DW     LZEQU
 891 ZGRET:          .DW     JZGRET
 892 JZGRET:         PKRM03 +/− FX<0 STF
 893 STT:            1 +/− PKM03 KGOTO9              ; [SP] := −1; NEXT
 894 
 895 JZLESS:                                         ; Обработчик 0<
 896                 PKRM03 FX>=0 STT
 897                 Cx PKM03 KGOTO9
 898 
 899 ;#ZeroLess
 900 ; 0<             ( x −− f )                           Проверка на отрицательность.
 901 LZLESS:         .DB     2
 902                 .TEXT   "0<"                    ; ( x −− флаг )
 903                 .DW     LZGRET
 904 ZLESS:          .DW     JZLESS
 905 
 906 ;#Equal
 907 ; =              ( y x −− f )                         Равно. Проверка на равенство.
 908 ; В стандарте Форта слово = сравнивает значения на вершине стека побитово.
 909 LEQUAL:         .DB     1
 910                 .TEXT   "="                     ; ( a b −− флаг )
 911                 .DW     LZLESS
 912 EQUAL:          .DW     JEQUAL
 913 JEQUAL:         RM3 M8 1 + M3
 914                 PKRM03 KRM8 −  PX!=0 STT
 915                 Cx PKM03 KGOTO9
 916 
 917 ;#Less
 918 ; <              ( y x −− f )                         Меньше. f равно −1 если и только если y меньше, чем x.
 919 LLESS:          .DB     1
 920                 .TEXT   "<"                     ; ( a b −− флаг ) F83
 921                 .DW     LEQUAL
 922 LESS:           .DW     JLESS
 923 JLESS:          RM3 M8 1 + M3
 924                 PKRM03 KRM8 − PX>=0 STT
 925                 Cx PKM03 KGOTO9
 926 
 927 ;#More
 928 ; >              ( y x −− f )                         Больше. f равно −1 если и только если y больше, чем x.
 929 LGREAT:         .DB     1
 930                 .TEXT   ">"                     ; ( a b −− флаг )
 931                 .DW     LLESS
 932 GREAT:          .DW     JGREAT
 933 JGREAT:         RM3 M8 1 + M3
 934                 KRM8 PKRM03 − PX>=0 STT
 935                 Cx PKM03 KGOTO9
 936 
 937 ;#ENCLOSE
 938 ; ENCLOSE        ( a1 c −− a2 u1 u2 )                 Окружить. Выделить лексему с адреса a1 и ограничительным символом c.
 939 ;               ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
 940 ; Делает смелое предположение, что дело происходит не в памяти программ
 941 ; Возможно, стоит разделить на пробел и не пробел или вообще переписать с нуля, пользуясь автоинкриментом.
 942 ; В Форте−2012 передаётся размер строки и нет специального значения нуля.
 943 LENCL:          .DB     7
 944                 .TEXT   "ENCLOSE"               ; ENCLOSE  ( a1 c −− a2 len da ) Форт ИТЭФ
 945                 .DW     LGREAT
 946 ENCL:           .DW     JENCL
 947 JENCL:          RM3 MA 1 + MB
 948                 KRMA M0                         ; R0 Разделитель
 949                 KRMB 1 EE 4 − M8 M5             ; R5 Начальный адрес
 950 A:              Cx KRM8 RM0 −                   ; Обход разделителей в начале
 951                 FX=0 NOTEQ
 952 AAA:            RM8 1 + M8 PGOTO A
 953 NOTEQ:          RM0 32 − FX=0 ENCLNSP           ; Только если пробел, проверять другие символы
 954                 KRM8 9 − FX!=0 AAA              ;  9 TAB
 955                 1 − FX!=0 AAA                   ; 10 LF
 956                 3 − FX!=0 AAA                   ; 13 CR
 957 ENCLNSP:        Cx EE 4 RM8 M7 + KMB            ; Начало лексемы
 958                 RM0 32 − FX!=0 AASP
 959 AA:             Cx KRM8 FX!=0 ZZZ               ; Если нуль
 960                 RM0 − FX!=0 EQW
 961                 RM8 1 + M8 PGOTO AA
 962 AASP0:          RM8 1 + M8
 963 AASP:           Cx KRM8 FX!=0 ZZZ               ; Если нуль
 964                 32 − FX!=0 EQW                  ; 32 пробел
 965                 PX<0 AASP0                      ; Пропускаем всё, что больше пробела
 966                 23 + FX!=0 EQW                  ;  9 TAB
 967                 1 − FX!=0 EQW                   ; 10 LF
 968                 3 − FX=0 AASP0                  ; 13 CR
 969 EQW:            RM8 RM7 − KMA
 970                 RM8 1 + RM5 − KM3
 971                 KGOTO9
 972 ZZZ:            RM8 RM7 − FX=0 EQW0
 973                 EE                              ; 0 −> 1
 974 EQW0:           KMA
 975                 RM8 RM5 − KM3
 976                 KGOTO9
 977 
 978 ;               ∗∗ Дисплей: вывод текста на индикатор ∗∗
 979 ;
 980 ;#EMI
 981 ; EMI            ( c n −− )                           Вывод нескольких символов на индикатор.
 982 LEMI:           .DB     3
 983                 .TEXT   "EMI"
 984                 .DW     LENCL                   ; Слово FORTH ИТЭФ
 985 EMI:            .DW     JEMI
 986 JEMI:           PKRM03 M0                       ; Число символов
 987                 RM3 1 + M8 1 + M3
 988                 RM0 +/− KX<09                   ; Проверка на отрицательное или нулевое число символов
 989 EMIS1:          KRM8 PGSB CHPUT                 ; Вывод символа         
 990                 FL0 EMIS1
 991                 KGRPH KGOTO9
 992 
 993 ;#EMIT
 994 ; EMIT           ( c −− )                             Отобразить символ.
 995 ; Отображает на экране символ, код которого находится в стеке.
 996 LEMIT:          .DB     4
 997                 .TEXT   "EMIT"                  ; F79
 998                 .DW     LEMI
 999 EMIT:           .DW     JEMIT
1000 JEMIT:          PKRM03 PGSB CHPUT
1001 CHPUTC3:        KGRPH                           ; Медленно, но пока так
1002                 PGOTO JDROP
1003 
1004 JCDOT:          PKRM03 MA                       ; Обработчик C.
1005                 PPRM9000 MB 121 − FX>=0 CHPUTC2 ; Грубая проверка, что символ уместится −− потом доработать
1006                 Cx MB PPM9000                   ; CR, по достижении конца строки
1007                 PGSB CHPUTLF                    ; LF
1008 CHPUTC2:        RMA PPM9020                     ; Вывести символ
1009                 PPRM9000 RMB − FX=0 CHPUTC3     ; Курсор сдвинулся с места?
1010                 Cx PPM9020                      ; Вывести '.'
1011                 GOTO CHPUTC3
1012 
1013 ;#Cd
1014 ; C.             ( c −− )                             Отобразить символ, заменяя управляющие на прямоугольники.
1015 ; Выводит символ как в дампе, управляющие символы заменяются на '.'
1016 LCDOT:          .DB     2
1017                 .TEXT   "C."                    ; К
1018                 .DW     LEMIT
1019 CDOT:           .DW     JCDOT
1020 
1021 ;#CR
1022 ; &crarr;              ( −− )                               Возврат каретки. Продолжить вывод с начала следующей строки.
1023 ; Ради оптимизации обработчик перенесён поближе к CHPUT
1024 LCR:            .DB     1,192                   ; К
1025                 .DW     LCDOT
1026 CR:             .DW     JCR
1027 
1028 ; ∗∗∗ Электронный звук ∗∗∗
1029 
1030 ;#BELL
1031 ; BELL           ( −− )                               Звонок. Озвучить гудок терминала.
1032 ; Ради оптимизации обработчик можно перенести поближе к CHPUT
1033 LBELL:          .DB     4
1034                 .TEXT   "BELL"                  ; F79
1035                 .DW     LCR
1036 BELL:           .DW     JBELL
1037 JBELL:          PGSB CHPUTBELL  KGOTO9
1038 
1039 ;#BEEP
1040 ; BEEP           ( p1 p2 −− )                         Пищать. Издать звук частотой p1 Гц и длительностью p2 &times; 10 мс.
1041 ; Лучше длительность сделать машинно−независимой −− в секундах или миллисекундах.
1042 LBEEP:          .DB     4
1043                 .TEXT   "BEEP"                  ; ( частота длительность −− )
1044                 .DW     LBELL
1045 BEEP:           .DW     JBEEP
1046 JBEEP:          RM3 MA 1 + MB 1 + M3
1047                 KRMA FX!=0 BPL2                 ; Нулевая длительность −− остановка воспроизведения
1048 BPL1:           PPRM 9052 FX=0 BPL1             ; Ожидание конца предыдущего сигнала
1049                 KRMB
1050 BPL2:           KRMA PPM 9052                   ; Формирование звукового сигнала
1051                 KGOTO9
1052 
1053 ;#xPLAY
1054 ; (PLAY)         ( a u −− )                           Играть музыку. Проиграть u нот, начиная с адреса a.
1055 ; Потом сделать не частоту, а ноту от 25 до 25000 Гц
1056 ; Также темп записать в переменную и умножать длительность на неё
1057 ; Расчёт стакатто/легатто и т.п. лучше сделать до компиляции мелодии на ММЯ
1058 ; В результате таблица будет занимать меньше места, состоя из 2−байтовых элементов
1059 LXPLAY:         .DB     6
1060                 .TEXT   "(PLAY)"                ; ( адрес число−нот −−)
1061                 .DW     LBEEP
1062 XPLAY:          .DW     JXPLAY
1063 JXPLAY:         PKRM03 KINT M0                  ; число нот
1064                 RM3 1 + MA 1 + M3
1065                 RM0 +/− KX<09                   ; число−нот должно быть положительным
1066                 KRMA 10001 − KX>=09 M5
1067                 9052 M8                         ; быстрее?  Надо проверить!
1068 XPL1:           KRM5 RME ∗ KRM5 + MA            ; 2 байта: длительность
1069                 KRM5 RME ∗ KRM5 + 10 / MB       ; 2 байта: частота∗10
1070 XPL2:           KRM8 FX=0 XPL2                  ; Ожидание конца предыдущего сигнала
1071                 RMB RMA KM8                     ; Формирование звукового сигнала
1072                 PFL0 XPL1
1073                 KGOTO9
1074 
1075 ; ∗∗∗ Машинная графика ∗∗∗
1076 
1077 ;#LIGHT
1078 ; LIGHT          ( −− )                               Установить вывод на индикатор светлым по тёмному.
1079 ; Выбрать светлый (зелёный) цвет для рисования (хорошо выглядит на тёмном фоне).
1080 ; Также делает прокрутку тёмной.
1081 LLIGHT:         .DB     5
1082                 .TEXT   "LIGHT"                 ; К
1083                 .DW     LXPLAY
1084 LIGHT:          .DW     JLIGHT
1085 JLIGHT:         4 PPM 9001  9008 PM19  KGOTO9
1086 
1087 ;#DARK
1088 ; DARK           ( −− )                               Установить вывод на индикатор тёмным по светлому.
1089 ; Выбрать тёмный (чёрный) цвет для рисования (хорошо выглядит на светлом фоне).
1090 ; Также делает прокрутку светлой.
1091 LDARK:          .DB     4
1092                 .TEXT   "DARK"                  ; К
1093                 .DW     LLIGHT
1094 DARK:           .DW     JDARK
1095 JDARK:          Cx PPM 9001  9007 PM19  KGOTO9
1096 
1097 ;#DOTStore
1098 ; DOT!           ( u1 u2 −− )                         Отобразить точку на индикаторе в колонке u1 строки u2.
1099 ; Отображение точки на экране (R9011)
1100 ; x y DOT! − значения координат x и y
1101 ; Позиция курсора не меняется.
1102 ; Атрибут из R9001. Обёртка вокруг R9011, но порядок аргументов взят из colorForth.
1103 LPLOT:          .DB     4
1104                 .TEXT   "DOT!"                  ; ( X Y −− )
1105                 .DW     LDARK
1106 PLOT:           .DW     JPLOT
1107 JPLOT:          11 ENT                          ; 9011 Вывод точки
1108 JPL0:           9 EE 3 + M8                     ; +++ В железном МК−161 после БП не нужен ENT
1109                 RM3 MB M5 2 + M3
1110                 KRMB KRM5 KM8
1111                 KGOTO9
1112 JFIX:           0 GOTO JPL0                     ; 9000 AT     Установить курсор
1113 JDRAW:          12 GOTO JPL0                    ; 9012 BAR    Вывод линии
1114 JBOX:           13 GOTO JPL0                    ; 9013 +BOX   Вывод прямоугольника
1115 JFRAME:         14 GOTO JPL0                    ; 9014 +FRAME Вывод рамки
1116 
1117 ;#BAR
1118 ; BAR            ( u1 u2 −− )                         Прочертить линию на индикаторе до точки (x,y)=(u1,u2).
1119 ; x y BAR рисует прямые линии (R9012), x и y − координаты конца линии
1120 ; Линия начинается из текущей позиции курсора, заданной AT
1121 ; Позиция курсора не меняется.
1122 ; Атрибут из R9001. Обёртка вокруг R9012, но порядок аргументов взят из colorForth.
1123 LDRAW:          .DB     3
1124                 .TEXT   "BAR"                   ; ( X Y −− )
1125                 .DW     LPLOT
1126 DRAW:           .DW     JDRAW
1127 
1128 ;#PlusBOX
1129 ; +BOX           ( u1 u2 −− )                         Нарисовать на индикаторе прямоугольник шириной u1 вышиной u2.
1130 ; dX dY +BOX рисует прямоугольник, dX и dY − размер прямоугольника
1131 ; Прямоугольник начинается от текущей позиции курсора, заданной AT
1132 ; Атрибут из R9001. Обёртка вокруг R9013, но порядок аргументов взят из colorForth.
1133 LBOX:           .DB     4
1134                 .TEXT   "+BOX"                  ; ( dX dY −− )
1135                 .DW     LDRAW
1136 BOX:            .DW     JBOX
1137 
1138 ;#PlusFRAME
1139 ; +FRAME         ( u1 u2 −− )                         Нарисовать на индикаторе рамку шириной u1 вышиной u2.
1140 ; dX dY +FRAME рисует рамку, dX и dY − размер рамки
1141 ; Рамка начинается от текущей позиции курсора, заданной AT
1142 ; Атрибут из R9001. Обёртка вокруг R9014, но порядок аргументов взят из colorForth.
1143 LFRAME:         .DB     6
1144                 .TEXT   "+FRAME"                ; ( dX dY −− )
1145                 .DW     LBOX
1146 FRAME:          .DW     JFRAME
1147 
1148 ;#gheeref
1149 ; ГРФ            ( −− )                               Вывести графическую информацию. Обновить индикатор.
1150 ; Вывод графической информации. Обётка вокруг К ГРФ. Слово, уникальное для Каллисто.
1151 LGRPH:          .DB     3,131,144,148           ; "ГРФ"
1152                 .DW     LFRAME
1153 GRPH:           .DW     JGRPH
1154 JGRPH:          KGRPH KGOTO9
1155 
1156 ;#PAGE
1157 ; &#8598;              ( −− )                               Очистить индикатор. Курсор в левый верхний угол.
1158 ; Очищаем экран, курсор в левый верхний угол
1159 ; Шрифт не меняем
1160 LPAGE:          .DB     1,200                   ; К
1161                 .DW     LGRPH
1162 PAGE:           .DW     JPAGE
1163 
1164 ;#AT
1165 ; AT             ( u1 u2 −− )                         Установить курсор в столбец u1 строки u2.
1166 ; Позиционирование курсора: x y AT
1167 LFIX:           .DB     2
1168                 .TEXT   "AT"                    ; ( x y −− )
1169                 .DW     LPAGE
1170 FIX:            .DW     JFIX
1171 
1172 ;#FONTStore
1173 ; FONT!          ( u −− )                             Установить на индикаторе шрифт u (0−2).
1174 ; Установка шрифта на индикаторе МК−161 и сопутствующего ему описания, см. _FONT
1175 LFONTSTO:       .DB     5
1176                 .TEXT   "FONT!"
1177                 .DW     LFIX
1178 FONTSTO:        .DW     JFONTSTO
1179 JFONTSTO:       RM3 MA 1 + M3                   ; Убираем u из стека
1180                 .NUM    rrCHWM1
1181                 M5                              ; R5 указывает на 5 байт, описывающих шрифт
1182                 KRMA                            ; RX: номер шрифта, который устанавливаем
1183                 PPM9003                         ; Установим заказанный шрифт
1184                 FX=0 FS12                       ; Это шрифт 0?
1185                 8 KM5 KM5  6 KM5  1 KM5 Cx KM5  ; Описание шрифта 0
1186                 KGOTO9
1187 FS12:           1 − PX=0 FS2                    ; Это шрифт 1?
1188                 8 KM5  14 KM5  9 KM5  2 KM5 KM5 ; Описание шрифта 1
1189                 KGOTO9
1190 FS2:            19 KM5 KM5 12 KM5 3 KM5 5 KM5   ; Описание шрифта 2
1191                 KGOTO9                          ; А самопальные шрифты пусть сами устанавливаются
1192 
1193 ;#DISKOFF
1194 ; DISKOFF        ( −− )                               Запрет дисковых операций.
1195 LDISKOFF:       .DB     7
1196                 .TEXT   "DISKOFF"               ; К
1197                 .DW     LFONTSTO
1198 DISKOFF:        .DW     JDISKOFF
1199 JDISKOFF:       Cx PPM 9120 KGOTO9
1200 
1201 ;               ∗∗ Клавиатура ∗∗
1202 
1203 ;#KEY
1204 ; KEY            ( −− c )                             Ввод символа с клавиатуры.
1205 ; Ожидает ввод символа с клавиатуры, при вводе посылает его код в стек.
1206 LKEY:           .DB     3
1207                 .TEXT   "KEY"                   ; F79
1208                 .DW     LDISKOFF
1209 KEY:            .DW     JKEY
1210 
1211 ;#EKEY
1212 ; EKEY           ( −− c )                             Ожидание нажатия клавиши и получение её кода.
1213 ; Чтение клавиатуры МК−161 −− клавиш, а не символов.
1214 LEKEY:          .DB     4
1215                 .TEXT   "EKEY"
1216                 .DW     LKEY
1217 EKEY:           .DW     JEKEY
1218 JEKEY:          KM3
1219 EKEYL:          PPRM 9029
1220                 PKM03
1221                 KNOT FX!=0 EKEYL
1222                 KGOTO9
1223 
1224 ;#INKEY
1225 ; INKEY          ( −− b )                             Получить код нажатой клавиши или −1, если клавиши не нажаты.
1226 ; Чтение состояния клавиатуры МК−161
1227 LINKEY:         .DB     5
1228                 .TEXT   "INKEY"
1229                 .DW     LEKEY
1230 INKEY:          .DW     JINKEY
1231 JINKEY:         PPRM 9028
1232                 KM3
1233                 KNOT KX=09
1234                 PGOTO STT                       ; 1 +/− PKM03  KGOTO9
1235 
1236 ;#ACCEPT
1237 ; ACCEPT         ( a c1 −− c2 )                       Ввести с клавиатуры строку в буфер с адресом a длиной c1. c2 −− число введённых литер.
1238 ; Этот код большой и важный, его можно вынести в BIOS.
1239 ; В конце два нуля не добавляются, теперь это обязанность вызывающей стороны.
1240 ; Теоретически в стеке возвратов можно хранить только "толщину" символов.
1241 ; Можно предусмотреть редактирование предыдущей строки, если такая была в буфере.
1242 LACCE:          .DB     6
1243                 .TEXT   "ACCEPT"                ; ( a n1 −− n2 )
1244                 .DW     LINKEY
1245 ACCE:           .DW     JACCE
1246 JACCE:          RM3 MA 1 + MB M3                ; Можно встроить проверку, куда идёт запись
1247                 KRMB 4 F10^X − MB               ; RB − адрес, куда записываются символы
1248                 KRMA M7                         ; R7 − максимальное количество символов
1249                 PPRM rrSCRLN                    ; Заранее готовим данные для прокрутки
1250                 8 ∗ M4                          ; R4 − на сколько линий корректируем y при прокрутке
1251                 RM2 M1                          ; R1 − буфер в стеке возвратов, хранящий "откат"
1252 ACCST:          CX M0                           ; R0 − количество введённых символов
1253 EXPE0:          PGSB CHGET
1254                 MA 8 − FX=0 EXPE1               ; Введён код BS?
1255                 RM0 FX!=0 EXPE0                 ; Если ничего не введено, игнорировать
1256                 1 − M0  PGSB ACCBS              ; Удалим последний символ
1257 JEXPE0:         GOTO EXPE0
1258 EXPE1:          5 − FX=0 EXPE2                  ; ENTER?
1259                 RM0 PKM03  KGOTO9               ; Возвращаем длину строки
1260 EXPE2:          114 − FX=0 ACCECH               ; F CX?
1261                 RM0 FX!=0 EXPE0                 ; Если ничего не введено, игнорировать
1262 ACCLP:          PGSB ACCBS
1263                 FL0 ACCLP
1264                 GOTO ACCST
1265 ACCECH:
1266                 RM0 RM7 − FX<0 EXPE0            ; Введены все символы?
1267                 RM0 1 + M0                      ; Увеличим счётчик символов
1268                 RMA KMB RMB 1 + MB              ; Добавим символ к строчке
1269                 PPRM9000                        ; Берём координаты курсора
1270                 129 PPRM rrCHW −                ; Предельная координата по горизонтали
1271                 − PX>=0 EXPE3                   ; В конце экранной строки?
1272                 <−> PPRM rrCHH +                ; Увеличиваем на высоту данного шрифта
1273                 65 FANS − − PX<0 EXPE4          ; На последней строке?
1274                 FANS + 0 PPM9000                ; Просто опускаемся на строчку вниз, в начало
1275                 GOTO EXPE3
1276 EXPE4:          PGSB CHPUTCR                    ; Курсор в начало строки
1277                 PGSB CHPUTSCROLL                ; Прокрутка экрана нужным цветом на нужное число строк
1278                 RM1                             ; Начало буфера откатов
1279 EXPE5:          M8 RM2 − FX<0 EXPE3             ; Всё скорректировали?
1280                 KRM8 RM4 − 0 KMAX KM8           ; y −= 8 ∗ кол−во линий прокрутки или 0
1281                 RM8 3 + GOTO EXPE5
1282 EXPE3:                                          ; Вывод обычного символа на экран
1283                 RM1 3 − M1 1 − M5               ; Новый элемент в буфере откатов
1284                 PPRM9000 <−> KM5 FANS KM5       ; Записываем Y, X; X в RX1
1285                 RMA PPM9020                     ; Выводим символ на индикатор
1286                 PPRM9000 FANS − KM5             ; Записываем dX
1287                 PGOTO EXPE0
1288 ACCBS:                                          ; Клавиша Cx, делаем "пробел назад" (BS)
1289                 RMB 1 − MB                      ; Укоротить введённую строку, можно использовать FL1 ?
1290                 RM1 3 + M1 4 − M5               ; Укоротить буфер откатов на 3 байта
1291                 KRM5 KRM5 PPM9000               ; Считать y, x; Установить курсор на предыдущий символ
1292                 4 PPRM9001 KXOR                 ; Сохранить атрибуты вывода в RX1
1293                 PPM9001                         ; Установить атрибут 4 для DARK и 0 для LIGHT
1294                 PPRM rrCHH KRM5 PPM9013         ; Шрифтовой dy; Считать dX; Вывод прямоугольника (dX может быть равен нулю)
1295                 FANS PPM9001                    ; Восстановить атрибуты вывода из X1
1296                 RTN
1297 
1298 ;               ∗∗ Арифметика ∗∗
1299 ;
1300 ;#OnePlus
1301 ; 1+             ( x −− x1 )                          Увеличить. x1 := x+1
1302 ; Прибавление 1.
1303 LONEP:          .DB     2
1304                 .TEXT   "1+"                    ; ( x −− x+1) F79
1305                 .DW     LACCE
1306 ONEP:           .DW     JONEP
1307 
1308 ;#TwoPlus
1309 ; 2+             ( x −− x1 )                          Прибавить два. x1 := x+2
1310 ; Прибавление 2.
1311 LTWOP:          .DB     2
1312                 .TEXT   "2+"                    ; ( x −− x+2) F79
1313                 .DW     LONEP
1314 TWOP:           .DW     JTWOP
1315 JTWOP:          2
1316                 .DB 59H                         ; FX>=0 aka пропустить 1 байт
1317 JONEP:          1
1318 NONEP:          PKRM03 + PKM03  KGOTO9
1319 JONEM:          1 +/− GOTO NONEP
1320 JTWOM:          2 +/− GOTO NONEP
1321 JMUL2:          PKRM03 GOTO NONEP
1322 
1323 ;#toBODY
1324 ; >BODY          ( т −− a )                           К телу. Преобразовать адрес поля кода в адрес поля параметров.
1325 ; Синоним 2+
1326 LGBODY:         .DB     5
1327                 .TEXT   ">BODY"                 ; ( cfa −− pfa) F83
1328                 .DW     LTWOP
1329 GBODY:          .DW     JTWOP
1330 
1331 ;#OneMinus
1332 ; 1−             ( x −− x1 )                          Уменьшить. x1 := x−1
1333 ; Вычитание 1.
1334 LONEM:          .DB     2
1335                 .TEXT   "1−"                    ; ( x −− x−1) F79
1336                 .DW     LGBODY
1337 ONEM:           .DW     JONEM
1338 
1339 ;#TwoTimes
1340 ; 2&times;             ( x −− x1 )                          Удвоить. x1 := x&times;2
1341 ; Умножение на 2 (двоичный сдвиг влево).
1342 ; В Форте обозначается 2∗ −− ещё со времён Форта−79.
1343 LMUL2:          .DB     2
1344                 .TEXT   "2"                     ; "2∗" ( x −− 2∗x) К
1345                 .DB     179                     ; Символ умножения МК−161
1346                 .DW     LONEM
1347 MUL2:           .DW     JMUL2
1348 
1349 ;#TwoDiv
1350 ; 2/             ( x −− n )                           Половина. n := Trunc [x/2]
1351 ; Целочисленное деление на 2 (двоичный сдвиг вправо).
1352 ; +++ Проверить работу с отрицательными числами.
1353 LDIV2:          .DB     2
1354                 .TEXT   "2/"                    ; ( n −− [n/2]) F79
1355                 .DW     LMUL2
1356 DIV2:           .DW     JDIV2
1357 JDIV2:          PKRM03 2 / KINT PKM03  KGOTO9
1358 
1359 ;#TwoMinus
1360 ; 2−             ( x −− x1 )                          Вычесть два. x1 := x−2
1361 ; Вычитание 2.
1362 LFDIV2:         .DB     2
1363                 .TEXT   "2−"                    ; ( x −− x−2) К
1364                 .DW     LDIV2
1365 TWOM:           .DW     JTWOM
1366 
1367 ;#Plus
1368 ; +              ( y x −− x1 )                        Сложить. x1 := y+x
1369 ; Сложение.
1370 LPLUS:          .DB     1
1371                 .TEXT   "+"                     ; ( a b −− a+b) F79
1372                 .DW     LFDIV2
1373 PLUS:           .DW     JPLUS
1374 JPLUS:          RM3 MB 1 + MA M3
1375                 KRMA KRMB + KMA
1376                 KGOTO9
1377 
1378 ;#Minus
1379 ; −              ( y x −− x1 )                        Вычесть. x1 := y−x
1380 ; Вычитание x из y.
1381 LSUB:           .DB     1
1382                 .TEXT   "−"                     ; ( a b −− a−b) F79
1383                 .DW     LPLUS
1384 SUB:            .DW     JSUB
1385 JSUB:           RM3 MB 1 + MA M3
1386                 KRMA KRMB − KMA
1387                 KGOTO9
1388 
1389 ;#Times
1390 ; &times;              ( y x −− x1 )                        Умножить. x1 := y&times;x
1391 ; Произведение чисел y и x. Присутствует ещё в Форте−79, но везде обозначается звёздочкой.
1392 LSTAR:          .DB     1,179                   ; "∗" Символ умножения на МК−161.
1393                 .DW     LSUB                    ; ( a b −− a∗b) К
1394 STAR:           .DW     JSTAR
1395 JSTAR:          RM3 MB 1 + MA M3
1396                 KRMA KRMB ∗ KMA
1397                 KGOTO9
1398 
1399 ;#Div
1400 ; &divide;              ( y x −− x1 )                        Разделить. x1 := y&divide;x
1401 ; Деление с десятичной плавающей запятой.
1402 LFDIV:          .DB     1,176                   ; Символ деления на МК−161.
1403                 .DW     LSTAR                   ; ( делимое делитель −− частное) К
1404 FDIV:           .DW     JFDIV                   ; ( a b −− a/b)
1405 JFDIV:          RM3 MB 1 + MA M3
1406                 KRMA KRMB / KMA
1407                 KGOTO9
1408 
1409 ;#Slash
1410 ; /              ( y x −− n )                         Делить нацело. n := Trunc [y/x]
1411 ; Целочисленное деление делимого x1 на делитель x2, помещение частного n в стек.
1412 ; +++ Проверить работу с отрицательными числами.
1413 LSLASH:         .DB     1
1414                 .TEXT   "/"                     ; ( делимое делитель −− частное) F79
1415                 .DW     LFDIV                   ; ( a b −− [a/b] )
1416 SLASH:          .DW     JSLASH
1417 JSLASH:         RM3 MB 1 + MA M3
1418                 KRMA KRMB / KINT KMA
1419                 KGOTO9
1420 
1421 ;#MOD
1422 ; MOD            ( y x −− x1 )                        Получение остатка от деления y на x. x1 := y mod x
1423 ; Получение остатка x1 от деления делимого y на делитель x.
1424 ; +++ Проверить работу с отрицательными числами.
1425 LMOD:           .DB     3
1426                 .TEXT   "MOD"                   ; ( делимое делитель −− остаток) F79
1427                 .DW     LSLASH
1428 MOD:            .DW     JMOD
1429 JMOD:           RM3 MB 1 + MA M3
1430                 KRMA ENT KRMB / KINT KRMB ∗ − KMA
1431                 KGOTO9
1432 
1433 ;#SlashMOD
1434 ; /MOD           ( y x −− y1 n )                      Делить y на x с частным n и остатком y1. n := Trunc [y/x], y1 := y mod x
1435 ; Деление с остатком делимого y на делитель x.
1436 ; +++ Проверить работу с отрицательными числами.
1437 LSLMOD:         .DB     4
1438                 .TEXT   "/MOD"                  ; ( делимое делитель −− остаток частное) F79
1439                 .DW     LMOD
1440 SLMOD:          .DW     JSLMOD
1441 JSLMOD:         RM3 MB 1 + MA
1442                 KRMA KRMA KRMB / FANS <−> KINT KMB ∗ − KMA
1443                 KGOTO9
1444 
1445 ;#TimesSlash
1446 ; &times;/             ( z y x −− n )                       Поделить произведение z&times;y на x. n := Trunc [x1&times;x2/x3]
1447 ; Операция масштабирования −− умножение z на y с последующим делением на x.
1448 ; Промежуточный результат умножения z∗y содержит 14 десятичных разрядов.
1449 ; Присутствует ещё в Форте−79, но везде обозначается ∗/
1450 LSSLA:          .DB     2
1451                 .DB     179                     ; Символ умножения МК−161.
1452                 .TEXT   "/"                     ; ( множитель1 множитель2 делитель −− частное) К
1453                 .DW     LSLMOD                  ; ( a b c −− [a∗b/c] )
1454 SSLA:           .DW     JSSLA
1455 JSSLA:          RM3 M8 1 + MA 1 + MB M3
1456                 KRMB KRMA ∗ KRM8 / KINT KMB
1457                 KGOTO9
1458 
1459 ;#TimesSlashMOD
1460 ; &times;/MOD          ( z y x −− y1 n )                    Получение частного n и остатка y1 от деления произведения z&times;y на x.
1461 ; Остаток y1 и частное n от деления произведения z∗y на x.
1462 ; Промежуточный результат z∗y содержит 14 десятичных разрядов.
1463 LSSMOD:         .DB     5
1464                 .DB     179                     ; Символ умножения МК−161.
1465                 .TEXT   "/MOD"                  ; "∗/MOD" ( множитель1 множитель2 делитель −− остаток частное) К
1466                 .DW     LSSLA
1467 SSMOD:          .DW     JSSMOD
1468 JSSMOD:         RM3 M8 1 + MA M3 1 + MB
1469                 KRMB KRMA ∗ ENT KRM8 / FANS <−> KINT KMA ∗ − KMB
1470                 KGOTO9
1471 
1472 ;#NEGATE
1473 ; /−/            ( x −− x1 )                          Смена знака. x1 := −x
1474 ; Смена знака числа в стеке.
1475 LNEGATE:        .DB     3
1476                 .TEXT   "/−/"                   ; ( x −− −x) К
1477                 .DW     LSSMOD
1478 NEGATE:         .DW     JNEGATE
1479 JNEGATE:        PKRM03 +/− PKM03  KGOTO9
1480 
1481 ;#SWAB
1482 ; ><             ( u −− u1 )                          Обменять старший и младший байты в 16−битном числе u.
1483 ; Предусмотреть отрицательные и запредельные числа.
1484 LSWAB:          .DB     2
1485                 .TEXT   "><"                    ; F79
1486                 .DW     LNEGATE
1487 SWAB:           .DW     JSWAB
1488 JSWAB:          RM3 M8
1489                 KRM8 ENT RME / FANS <−> KINT MA ∗ −
1490                 RME ∗ RMA + KM8  KGOTO9
1491 
1492 ;#StoD
1493 ; S>D            ( n −− u1 u2 )                       Преобразовывает 32−битное число n в пару 16−битных чисел u2 u1.
1494 ; Немного сложнее DUP 0< т.к. мы преобразовываем 12−разрядные десятичные числа, а не 16−битные двоичные.
1495 ; +++ Можно сократить, сделав RME FX^2 вместо ENT 65536.
1496 LSTOD:          .DB     3
1497                 .TEXT   "S>D"                   ; ( n −− dd ) F12
1498                 .DW     LSWAB
1499 STOD:           .DW     JSTOD
1500 JSTOD:          RM3 M8 KRM8 PGSB Norm32
1501                 ENT ENT 65536 / FANS <−> KINT KM3 ∗ − KM8  KGOTO9
1502 
1503 ;#ABS
1504 ; |X|            ( x −− p )                           Вычисление модуля. p := |x|
1505 ; Получение абсолютного значения.
1506 ; В Форте−79 и последующих называется ABS
1507 LABS:           .DB     3
1508                 .TEXT   "|X|"                   ; ( x −− |x| ) К
1509                 .DW     LSTOD
1510 ABS:            .DW     JABS
1511 JABS:           PKRM03 KABS PKM03  KGOTO9
1512 
1513 ;#AND
1514 ; AND            ( n1 n2 −− i )                       32−битное логическое умножение (и). i := n1 &amp; n2
1515 ; 32−битное побитовое логическое умножение (И).
1516 ; Цикл можно развернуть и оптимизировать, если будет достаточно памяти.
1517 LSAND:          .DB     3
1518                 .TEXT   "AND"                   ; ( a b −− a & b ) F79
1519                 .DW     LABS
1520 SAND:           .DW     JSAND
1521 JSAND:          RM3 MB 1 M1 + MA M3
1522                 CX M8 4 M0
1523                 KRMB PGSB Norm32 MB
1524                 KRMA PGSB Norm32 MA
1525                 GOTO SANDIN
1526 SANDL:          M8  RM1 RME ∗ M1
1527                 RMA
1528 SANDIN:             ENT RME / FANS <−> KINT MA ∗ − 
1529                 RMB ENT RME / FANS <−> KINT MB ∗ − 
1530                 KAND RM1 ∗ RM8 +
1531                 PFL0 SANDL
1532                 GSB Sign32
1533                 PKM03 KGOTO9
1534 
1535 ; Приводит 32−битное число в беззнаковый вид.
1536 ; Можно также подогнать под двоичные машины переполнения, в обе стороны.
1537 Norm32:         FX<0 N32A
1538                 65536 FX^2 +                    ; FX^2 vs ENT ∗
1539 N32A:           RTN
1540 
1541 ;#OR
1542 ; OR             ( n1 n2 −− i )                       32−битное логическое сложение (или). i := n1 &#8744; n2
1543 ; 32−битное побитовое логическое сложение (ИЛИ).
1544 LSOR:           .DB     2
1545                 .TEXT   "OR"                    ; ( a b −− a OR b ) F79
1546                 .DW     LSAND
1547 SOR:            .DW     JSOR
1548 JSOR:           RM3 MB 1 M1 + MA M3
1549                 CX M8 4 M0
1550                 KRMB GSB Norm32 MB
1551                 KRMA GSB Norm32 MA
1552                 GOTO SORIN
1553 SORL:           M8  RM1 RME ∗ M1
1554                 RMA
1555 SORIN:              ENT RME / FANS <−> KINT MA ∗ − 
1556                 RMB ENT RME / FANS <−> KINT MB ∗ − 
1557                 KOR RM1 ∗ RM8 +
1558                 FL0 SORL
1559                 GSB Sign32
1560                 PKM03 KGOTO9
1561 ;
1562 ; Преобразование 32−битного беззнакового в 32−битное знаковое.
1563 Sign32:
1564                 32768 ENT 65536 ∗               ; 2 147 483 648
1565                 − FX<0 S32Fix
1566                 FANS + RTN                      ; Восстановить исходное число
1567 S32Fix:         FANS − RTN                      ; Преобразовать
1568 
1569 ;#XOR
1570 ; XOR            ( n1 n2 −− i )                       32−битное сложение по модулю два (исключающее или). i := n1 &#8853; n2
1571 ; 32−битное побитовое логическое исключающее ИЛИ (XOR).
1572 LSXOR:          .DB     3
1573                 .TEXT   "XOR"                   ; ( a b −− a XOR b) F79
1574                 .DW     LSOR
1575 SXOR:           .DW     JSXOR
1576 JSXOR:          RM3 MB 1 M1 + MA M3
1577                 CX M8 4 M0
1578                 KRMB PGSB Norm32 MB
1579                 KRMA PGSB Norm32 MA
1580                 GOTO SXORIN
1581 SXORL:          M8  RM1 RME ∗ M1
1582                 RMA
1583 SXORIN:             ENT RME / FANS <−> KINT MA ∗ − 
1584                 RMB ENT RME / FANS <−> KINT MB ∗ − 
1585                 KXOR RM1 ∗ RM8 +
1586                 FL0 SXORL
1587                 PGSB Sign32
1588                 PKM03 KGOTO9
1589 
1590 ;#NOT
1591 ; NOT            ( n −− i )                           32−битная логическая инверсия (не). i := ∼n
1592 ; 32−битное побитовое логическое отрицание (НЕ).
1593 LINVERT:        .DB     3
1594                 .TEXT   "NOT"                   ; ( n −− NOT n ) К
1595                 .DW     LSXOR
1596 INVERT:         .DW     JINVERT
1597 JINVERT:        PKRM03 PGSB Sign32
1598                 1 + +/− PKM03 KGOTO9            ; Отрицание через арифметику.
1599 
1600 ;#MIN
1601 ; MIN            ( y x −− x1 )                        Меньшее из двух чисел. x1 := min (x, y)
1602 ; Выделение минимума, обёртка вокруг K MAX.
1603 LMIN:           .DB     3
1604                 .TEXT   "MIN"                   ; ( a b −− min) F79
1605                 .DW     LINVERT
1606 MIN:            .DW     JMIN
1607 JMIN:           RM3 MB 1 + MA M3
1608                 KRMB KRMA KMAX <−> KMA
1609                 KGOTO9
1610 
1611 ;#MAX
1612 ; MAX            ( y x −− x1 )                        Большее из двух чисел. x1 := max (x, y)
1613 ; Выделение максимума, обёртка вокруг K MAX.
1614 LMAX:           .DB     3
1615                 .TEXT   "MAX"                   ; ( a b −− max) F79
1616                 .DW     LMIN
1617 MAX:            .DW     JMAX
1618 JMAX:           RM3 MB 1 + MA M3
1619                 KRMB KRMA KMAX KMA
1620                 KGOTO9
1621 
1622 ; ∗∗∗ Примитивы МК−161 ∗∗∗
1623 ;#FALOG
1624 ; 10&#739;            ( x −− p )                           Десятичный антилогарифм. p := 10^x
1625 ; Обёртка вокруг F10^X.
1626 LEXP10:         .DB     3
1627                 .TEXT   "10"                    ; ( x −− 10^x) К
1628                 .DB     190                     ; 10^X
1629                 .DW     LMAX
1630 EXP10:          .DW     JEXP10
1631 JEXP10:         PKRM03 F10^X PKM03  KGOTO9
1632 
1633 ;#FEXP
1634 ; E&#739;             ( x −− p )                           Экспонента. p := e^x
1635 ; Обёртка вокруг FEXP.
1636 LEXPE:          .DB     2
1637                 .TEXT   "E"                     ; ( x −− e^x) К
1638                 .DB     190                     ; E^X
1639                 .DW     LEXP10
1640 EXPE:           .DW     JEXPE
1641 JEXPE:          PKRM03 FEXP PKM03  KGOTO9
1642 
1643 ;#LG
1644 ; LG             ( p −− x )                           Десятичный логарифм. x := lg p
1645 ; Обёртка вокруг Flg.
1646 LLG:            .DB     2
1647                 .TEXT   "LG"                    ; ( x −− lg x) К
1648                 .DW     LEXPE
1649 LG:             .DW     JLG
1650 JLG:            PKRM03 FLG PKM03  KGOTO9
1651 
1652 ;#LN
1653 ; LN             ( p −− x )                           Натуральный логарифм. x := ln p
1654 ; Обёртка вокруг Fln.
1655 LLN:            .DB     2
1656                 .TEXT   "LN"                    ; ( x −− ln x) К
1657                 .DW     LLG
1658 LN:             .DW     JLN
1659 JLN:            PKRM03 FLN PKM03  KGOTO9
1660 
1661 ;#Xcaret2
1662 ; X&sup2;             ( x −− p )                           Квадрат числа. p := x^2
1663 ; Обёртка вокруг FX^2.
1664 LX2:            .DB     2
1665                 .TEXT   "X"                     ; ( x −− x^2) К
1666                 .DB     189                     ; X^2
1667                 .DW     LLN
1668 X2:             .DW     JX2
1669 JX2:            PKRM03 FX^2 PKM03  KGOTO9
1670 
1671 ;#FSQRT
1672 ; &radic;              ( p −− p1 )                          Квадратный корень. p1 := sqrt p
1673 ; Обёртка вокруг FSQRT.
1674 LSQRT:          .DB     1,251                   ; ( r −− sqrt r) К
1675                 .DW     LX2
1676 SQRT:           .DW     JSQRT
1677 JSQRT:          PKRM03 FSQRT PKM03  KGOTO9
1678 
1679 ;#OneDivX
1680 ; 1/X            ( x −− x1 )                          Обратная величина. x1 := 1/x
1681 ; Обёртка вокруг F1/x.
1682 LONEX:          .DB     3
1683                 .TEXT   "1/X"                   ; ( r −− 1/r) К
1684                 .DW     LSQRT
1685 ONEX:           .DW     JONEX
1686 JONEX:          PKRM03 F1/X PKM03  KGOTO9
1687 
1688 ;#FTimesTimes
1689 ; Y&#739;             ( p x −− x1 )                        Возведение в степень положительного числа. x1 := p^x
1690 ; Степенная функция, обёртка вокруг FX^Y.
1691 ; +++ Убрать артефакты ПМК, степенная функция там имеет проблемы!
1692 LPOWER:         .DB     2
1693                 .TEXT   "Y"                     ; Y^X ( y x −− y^x) К
1694                 .DB     190
1695                 .DW     LONEX
1696 POWER:          .DW     JPOWER
1697 JPOWER:         RM3 MB 1 + M3 MA
1698                 KRMB KRMA FX^Y KMA  KGOTO9
1699 
1700 ;#RND
1701 ; СЧ             ( −− p )                             Псевдослучайное число. 0<=p<1
1702 ; Обёртка вокруг KRAN.
1703 LRND:           .DB     2,145,151               ; "СЧ" ( −− x) К
1704                 .DW     LPOWER
1705 RND:            .DW     JRND
1706 JRND:           KRAN KM3  KGOTO9
1707 
1708 ;#SGN
1709 ; ЗН             ( x −− n )                           Знак числа. n := −1 / 0 / 1
1710 ; Обёртка вокруг KSGN.
1711 LSGN:           .DB     2,135,141               ; "ЗН" ( x −− sgn x) К
1712                 .DW     LRND
1713 SGN:            .DW     JSGN
1714 JSGN:           PKRM03 KSGN PKM03  KGOTO9
1715 
1716 ;#FTRUNC
1717 ; [X]            ( x −− x1 )                          Усечение (целая часть) числа. x1 := Trunc [x]
1718 ; Обёртка вокруг KINT.
1719 LINT:           .DB     3                       ; ( x −− [x]) К
1720                 .TEXT   "[X]"
1721                 .DW     LSGN
1722 INT:            .DW     JINT
1723 JINT:           PKRM03 KINT PKM03  KGOTO9
1724 
1725 ;#FRAC
1726 ; {X}            ( x −− x1 )                          Дробная часть числа. x1 := x − Trunc [x]
1727 ; Обёртка вокруг KFRAC.
1728 LFRAC:          .DB     3                       ; ( x −− {x}) К
1729                 .TEXT   "{X}"
1730                 .DW     LINT
1731 FRAC:           .DW     JFRAC
1732 JFRAC:          PKRM03 KFRAC PKM03  KGOTO9
1733 
1734 
1735 ; ∗∗∗ Тригонометрия МК−161 ∗∗∗
1736 ;
1737 ;#DEGREES
1738 ; DEGREES        ( −− )                               Градусы. Установить градусную меру измерения угла.
1739 ; Даёт указание Каллисто (и МК−161) принимать углы в градусах
1740 LDEGR:          .DB     7
1741                 .TEXT   "DEGREES"               ; MMSFORTH
1742                 .DW     LFRAC
1743 DEGR:           .DW     JDEGR
1744 JDEGR:          Cx GOTO SETANG
1745 
1746 ;#RADIANS
1747 ; RADIANS        ( −− )                               Радианы. Установить радианную меру измерения угла.
1748 ; Даёт указание Каллисто (и МК−161) принимать углы в радианах
1749 LRAD:           .DB     7
1750                 .TEXT   "RADIANS"               ; MMSFORTH
1751                 .DW     LDEGR
1752 RAD:            .DW     JRAD
1753 JRAD:           1
1754 SETANG:         PPM 9045 KGOTO9                 ; Задание размерности аргумента при вычислении тригонометрических функций
1755 
1756 ;#ARCSIN
1757 ; ARCSIN         ( x −− x1 )                          Арксинус. x1 := arcsin x
1758 ; Обёртка вокруг FARCSIN.
1759 LASIN:          .DB     6
1760                 .TEXT   "ARCSIN"                ; ( x −− arcsin x) К
1761                 .DW     LRAD
1762 ASIN:           .DW     JASIN
1763 JASIN:          PKRM03 FARCSIN PKM03  KGOTO9
1764 
1765 ;#ARCCOS
1766 ; ARCCOS         ( x −− x1 )                          Арккосинус. x1 := arccos x
1767 ; Обёртка вокруг FARCCOS.
1768 LACOS:          .DB     6
1769                 .TEXT   "ARCCOS"                ; ( x −− arccos x) К
1770                 .DW     LASIN
1771 ACOS:           .DW     JACOS
1772 JACOS:          PKRM03 FARCCOS PKM03  KGOTO9
1773 
1774 ;#ARCTG
1775 ; ARCTG          ( x −− x1 )                          Арктангенс. x1 := arctg x
1776 ; Обёртка вокруг FARCTG.
1777 LATG:           .DB     5
1778                 .TEXT   "ARCTG"                 ; ( x −− arctg x) К
1779                 .DW     LACOS
1780 ATG:            .DW     JATG
1781 JATG:           PKRM03 FARCTG PKM03  KGOTO9
1782 
1783 ;#SIN
1784 ; SIN            ( x −− x1 )                          Синус. x1 := sin x
1785 ; Обёртка вокруг FSIN.
1786 LSIN:           .DB     3
1787                 .TEXT   "SIN"                   ; ( x −− sin x) К
1788                 .DW     LATG
1789 SIN:            .DW     JSIN
1790 JSIN:           PKRM03 FSIN PKM03  KGOTO9
1791 
1792 ;#COS
1793 ; COS            ( x −− x1 )                          Косинус. x1 := cos x
1794 ; Обёртка вокруг FCOS.
1795 LCOS:           .DB     3
1796                 .TEXT   "COS"                   ; ( x −− cos x) К
1797                 .DW     LSIN
1798 COS:            .DW     JCOS
1799 JCOS:           PKRM03 FCOS PKM03  KGOTO9
1800 
1801 ;#TG
1802 ; TG             ( x −− x1 )                          Тангенс. x1 := tg x
1803 ; Обёртка вокруг FTG.
1804 LTG:            .DB     2
1805                 .TEXT   "TG"                    ; ( x −− tg x) К
1806                 .DW     LCOS
1807 TG:             .DW     JTG
1808 JTG:            PKRM03 FTG PKM03  KGOTO9
1809 
1810 ; ∗∗∗ Стек ∗∗∗
1811 ;
1812 ;#SPFetch
1813 ; SP@            ( −− a )                             Считать SP. Адрес текущей вершины стека данных.
1814 ; Записывает в стек адрес вершины стека, до исполнения команды SP@
1815 LSPAT:          .DB     3
1816                 .TEXT   "SP@"                   ; F79
1817                 .DW     LTG
1818 SPAT:           .DW     JSPAT
1819 JSPAT:          RM3
1820 SPAT0:          1 EE 4 + KM3  KGOTO9
1821 
1822 ;#StoreSP
1823 ; !SP            ( −− )                               Восстановить SP. Присвоить указателю стека данных исходное значение из переменной S0.
1824 ; Устанавливает указатель стека в исходное состояние из переменной S0.
1825 LSPSTO:         .DB     3
1826                 .TEXT   "!SP"
1827                 .DW     LSPAT
1828 SPSTO:          .DW     JSPSTO
1829 JSPSTO:         PPRM rrS0 RME ∗ PPRM rlS0 +     ; Считывание переменой S0
1830                 1 EE 4 − M3  KGOTO9             ; SP := S0
1831 
1832 ;               ∗∗ Стек возвратов ∗∗
1833 ;
1834 ;#RPFetch
1835 ; RP@            ( −− a )                             Считать RP. Адрес текущей вершины стека возвратов.
1836 LRPAT:          .DB     3
1837                 .TEXT   "RP@"
1838                 .DW     LSPSTO
1839 RPAT:           .DW     JRPAT
1840 JRPAT:          RM2 GOTO SPAT0
1841 
1842 ;#StoreRP
1843 ; !RP            ( −− )                               Восстановить RP. Присвоить указателю стека возвратов исходное значение из переменной R0.
1844 ; Присваивает указателю стека возвратов исходное (базовое) значение из переменной R0.
1845 LRPSTO:         .DB     3
1846                 .TEXT   "!RP"
1847                 .DW     LRPAT
1848 RPSTO:          .DW     JRPSTO
1849 JRPSTO:         PPRM rrR0 RME ∗ PPRM rlR0 +     ; Считывание переменной R0
1850                 1 EE 4 − M2  KGOTO9             ; RP := R0
1851 
1852 ;#EXIT
1853 ; EXIT           ( −− )                               Закончить исполнение текущего определения.
1854 ; При использовании EXIT внутри FOR .. NEXT нужно предварительно выполнить UNLOOP
1855 LEXIT:          .DB     4
1856                 .TEXT   "EXIT"                  ; F79
1857                 .DW     LRPSTO
1858 EXIT:           .DW     RETURN
1859 
1860 ;#LEAVE
1861 ; LEAVE          ( −− )                               Выйти из цикла FOR досрочно.
1862 ; Досрочно прерывает цикл FOR .. NEXT (только для области данных)
1863 ; В стандарте Форт−83 это слово IMMEDIATE −− в данной реализации Каллисто обычное.
1864 ; Можно изменить цикл FOR .. NEXT так, чтобы он работал быстрее, а LEAVE содержало адрес ячейки, следующей за NEXT
1865 LLEAVE:         .DB     5
1866                 .TEXT   "LEAVE"                 ; F83
1867                 .DW     LEXIT
1868 LEAVE:          .DW     JLEAVE
1869 JLEAVE:         RM2 1 + M5 3 + M2
1870                 KRM5 RME ∗ KRM5 + M6
1871                 KGOTO9
1872 
1873 ;#UNLOOP
1874 ; UNLOOP         ( −− )                               Снять со стека возвратов параметры управления циклом FOR для текущего уровня вложенности.
1875 ; Обычно используется перед EXIT для выхода изнутри цикла со счётчиком.
1876 LUNLOOP:        .DB     6
1877                 .TEXT   "UNLOOP"
1878                 .DW     LLEAVE
1879 UNLOOP:         .DW     JUNLOOP
1880 
1881 ;#toR
1882 ; >R             ( d −− )                             Перенести d на стек возвратов.
1883 ; Переносит 16−битное целое из стека данных в стек возвратов.
1884 LTOR:           .DB     2
1885                 .TEXT   ">R"                    ; ( n −− )  ( R: −− n ) F79
1886                 .DW     LUNLOOP
1887 TOR:            .DW     JTOR
1888 JTOR:           PKRM03 KINT
1889                 FX<0 TOR1
1890                 65536 +
1891 TOR1:           RME / ENT KFRAC RME ∗ KM2       ; +++ может возникнуть ошибка округления
1892                 FR KINT KM2
1893                 PGOTO JDROP
1894 
1895 ;#Rfrom
1896 ; R>             ( −− D )                             Перемещает D из стека возвратов на стек данных.
1897 ; Переносит 16−битное целое со знаком из стека возвратов в стек данных.
1898 LFROMR:         .DB     2
1899                 .TEXT   "R>"                    ; ( −− n)  ( R: n −−) F79
1900                 .DW     LTOR
1901 FROMR:          .DW     JFROMR
1902 JFROMR:         RM2 MA M5 2 + M2
1903 JFR2:           KRMA RME ∗ KRM5 + M0
1904                 32768 −
1905                 PX>=0 FROMR1
1906                 FANS − KM3 KGOTO9
1907 FROMR1:         RM0 KM3 KGOTO9
1908 
1909 ;#RFetch
1910 ; R@             ( −− D )                             Скопировать значение D со стека возвратов.
1911 ; Тоже, что и I −− но для целых со знаком.
1912 LRAT:           .DB     2
1913                 .TEXT   "R@"                    ; ( −− n)
1914                 .DW     LFROMR
1915 RAT:            .DW     JR
1916 JR:             RM2 MA M5 PGOTO JFR2
1917 
1918 ;#I
1919 ; I              ( −− U )                             Значение счётчика цикла FOR.
1920 ; Копирует верхнее 16−битное число из стека возвратов в стек данных.
1921 ; Название связано с использованием внутри циклов FOR .. NEXT −− в них I копирует в стек значение индекса цикла.
1922 LI:             .DB     1
1923                 .TEXT   "I"                     ; ( −− n) F79
1924                 .DW     LRAT
1925 I:              .DW     JI
1926 JI:             RM2
1927 I0:             MA M5
1928                 KRMA RME ∗ KRM5 + KM3 KGOTO9
1929 
1930 ;#J
1931 ; J              ( −− U )                             Значение счётчика внешнего цикла FOR.
1932 ; Узнать значение переменной цикла из вложенного цикла.
1933 LJ:             .DB     1
1934                 .TEXT   "J"                     ; ( −− n)
1935                 .DW     LI
1936 J:              .DW     JJ
1937 JJ:             4
1938 JJ0:            RM2 + GOTO I0
1939 
1940 ;#K
1941 ; K              ( −− U )                             Значение счётчика третьего объемлющего цикла FOR.
1942 ; Узнать значение переменной цикла из дважды вложенного цикла.
1943 LK:             .DB     1                       ; ( −− n)
1944                 .TEXT   "K"
1945                 .DW     LJ
1946 K:              .DW     JK
1947 JK:             8 GOTO JJ0
1948 
1949 ;#RDROP
1950 ; RDROP          ( −− )                               Удалить верхний адрес со стека возвратов.
1951 LLEV:           .DB     5
1952                 .TEXT   "RDROP"
1953                 .DW     LK
1954 LEV:            .DW     JLEV
1955 
1956 ;               ∗∗ Операции со стеком параметров ∗∗
1957 ;
1958 ;#PICK
1959 ; PICK           ( xu .. x1 x0 u −− xu .. x1 x0 xu )  Дублировать u−ное сверху.
1960 ; Копирует элемент стека с номером u и записывает его наверх стека.
1961 ; Следуя стандарту Форт−83 отсчёт начинается сверху, верхний элемент имеет номер 0, то есть 0 PICK это DUP
1962 LPICK:          .DB     4
1963                 .TEXT   "PICK"                  ; F83
1964                 .DW     LLEV
1965 PICK:           .DW     JPICK
1966 JPICK:          PKRM03 KX>=09                   ; Проверка на неверное значение
1967                 RM3 + M5 KRM5 PKM03             ; Косвенная адресация с R5 использует прединкремент
1968                 KGOTO9
1969 
1970 ;#OVER
1971 ; OVER           ( y x −− y x y )                     Дублировать второй сверху.
1972 ; Копировать 2−й сверху элемент стека и занести его наверх.
1973 LOVER:          .DB     4
1974                 .TEXT   "OVER"                  ; ( a b −− a b a) F79
1975                 .DW     LPICK
1976 OVER:           .DW     JOVER
1977 JOVER:          RM3 M5 KRM5 KM3  KGOTO9
1978 
1979 ;#SWAP
1980 ; &harr;              ( y x −− x y )                       Обмен.
1981 ; Меняет местами два верхних элемента стека.
1982 LSWAP:          .DB     1,183                   ; ( a b −− b a) К
1983                 .DW     LOVER
1984 SWAP:           .DW     JSWAP
1985 JSWAP:          RM3 M8 M5
1986                 KRM5 KRM8 PKM05 <−> KM8
1987                 KGOTO9
1988 
1989 ;#TwoSWAP
1990 ; 2SWAP          ( t z y x −− y x t z )               Обмен двух пар.
1991 ; Меняет местами верхние две пары чисел в стеке.
1992 ; Возможно, использование R5 поможет сократить и ускорить код.
1993 LDSWAP:         .DB     5
1994                 .TEXT   "2SWAP"                 ; ( dd1 dd2 −− dd2 dd1) F79
1995                 .DW     LSWAP
1996 DSWAP:          .DW     JDSWAP
1997 JDSWAP:         RM3 M8 2 + MA
1998                 KRMA KRM8 KMA <−> KM8
1999                 RM3 1 + M8 2 + MA
2000                 KRMA KRM8 KMA <−> KM8
2001                 KGOTO9
2002 
2003 ;#DUP
2004 ; &uarr;              ( x −− x x )                         Дублировать.
2005 ; Дублирует верхний элемент стека.
2006 LDUP:           .DB     1,24                    ; К
2007                 .DW     LDSWAP
2008 DUP:            .DW     JDUP
2009 JDUP:           PKRM03 KM3  KGOTO9
2010 
2011 ;#qDUP
2012 ; ?DUP           ( x −− 0 | x x )                     Дублировать, если x<>0.
2013 ; Осуществляет операцию DUP если x<>0
2014 LQDUP:          .DB     4
2015                 .TEXT   "?DUP"                  ; F79
2016                 .DW     LDUP
2017 QDUP:           .DW     JQDUP
2018 JQDUP:          PKRM03  KX!=09
2019                 KM3  KGOTO9
2020 
2021 ;#TwoDUP
2022 ; 2DUP           ( y x −− y x y x )                   Дублировать пару чисел.
2023 ; Дублирует верхнюю пару чисел в стеке.
2024 ; : 2DUP  OVER OVER ;
2025 LDUP2:          .DB     4
2026                 .TEXT   "2DUP"                  ; ( dd −− dd dd ) F79
2027                 .DW     LQDUP
2028 DUP2:           .DW CALL, OVER,OVER, EXIT       ; Экономим 2 байта
2029 ;JDUP2:         RM3 M8 M5
2030 ;               KRM5 KM3 KRM8 KM3
2031 ;               KGOTO9
2032 
2033 ;#ROT
2034 ; ROT            ( z y x −− y x z )                   Вращать. Перенести наверх третий сверху.
2035 ; Переносит 3−й элемент стека наверх. ("Вращение трёх верхних элементов по часовой стрелке.")
2036 LROT:           .DB     3
2037                 .TEXT   "ROT"                   ; ( a b c −− b c a) F79
2038                 .DW     LDUP2
2039 ROT:            .DW     JROT
2040 JROT:           RM3 M8 M5
2041                 KRM5 KRM8 PKM05 <−>
2042                 KRM5 KM8 <−>
2043                 PKM05
2044                 KGOTO9
2045 ; x1 x2 x3 −− a b c = x2 x3 x1
2046 ;
2047 ; T |
2048 ; Z |                         x3   x3    x3    x3
2049 ; Y |        x2   x2     x3   x2   x2    x1    x1
2050 ; X |  x2    x3  b:=x3   x2   x1  c:=x1  x2   a:=x2
2051 ; −−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
2052 ;     KRM5  KRM8 PKM05  <−>  KRM5  KM8   <−>  PKM05
2053 
2054 ;#TwoDROP
2055 ; 2DROP          ( y x −− )                           Убрать пару чисел.
2056 ; Удаляет из стека число двойной длины.
2057 LDDROP:         .DB     5
2058                 .TEXT   "2DROP"                 ; ( dd −− ) F79
2059                 .DW     LROT
2060 DDROP:          .DW     JDDROP
2061 JDDROP:         2 PGOTO NDROP
2062 
2063 ;#DROP
2064 ; DROP           ( x −− )                             Убрать. Удалить верхний элемент стека.
2065 LDROP:          .DB     4
2066                 .TEXT   "DROP"                  ; ( x −− ) F79
2067                 .DW     LDDROP
2068 DROP:           .DW     JDROP                   ; JDROP: 1 RM3 + M3  KGOTO9
2069 
2070 ;               ∗∗ Работа с памятью ∗∗
2071 ;
2072 ;#CMOVE
2073 ; CMOVE          ( a1 a2 u −− )                       Копировать u байтов из a1 в a2.
2074 ; Копирует побайтно область памяти размером u байт начиная с a1 и записывает её начиная с a2.
2075 ; Сперва копируем содержимое байта с адресом a1, продолжаем в сторону больших адресов.
2076 ; Мы считаем, что блок не пересекает границ адресных пространств.
2077 LCMOVE:         .DB     5
2078                 .TEXT   "CMOVE"                 ; ( откуда куда сколько −− ) F79
2079                 .DW     LDROP
2080 CMOVE:          .DW     JCMOVE
2081 JCMOVE:         PKRM03 M0                       ; Счётчик
2082                 RM3 1 + M8 1 + MA 1 + M3
2083                 RM0 +/− KX<09
2084                 KRM8 1 EE 4 − KX>=09 1 − M5     ; Куда    ( только в память данных)
2085                 KRMA MA 1 EE 4 − FX<0 CMOVER0   ; Откуда
2086                 RMA
2087 CMOVEP:         KPRGM KM5  RMA 1 + MA
2088                 FL0 CMOVEP  KGOTO9
2089 CMOVER0:        1 − M4                          ; Прединкримент
2090 CMOVERL:        Cx KRM4 KM5 FL0 CMOVERL  KGOTO9
2091 
2092 ;#CMOVEtop
2093 ; CMOVE>         ( a1 a2 u −− )                       Копировать u байтов из a1 в a2, начиная с больших адресов.
2094 LCMOVEG:        .DB     6
2095                 .TEXT   "CMOVE>"                ; ( откуда куда сколько −−)
2096                 .DW     LCMOVE
2097 CMOVEG:         .DW     JCMOVEG
2098 JCMOVEG:        PKRM03 M0                       ; Счётчик
2099                 RM3 1 + MB 1 + MA 1 + M3 M8     ; R8 := SP (R3)
2100                 KRMB 1 EE 4 − KX>=09
2101                 RM0 +/− KX<09 − M1              ; R1 := Куда, конец блока
2102                 KRMA MA 1 EE 4 − FX<0 CMVGR0
2103                 RMA RM0 +
2104 CMVGP:          MA KPRGM KM1 RMA 1 − FL0 CMVGP KGOTO9
2105 CMVGR0:         RM0 + M3
2106 CMVGRL:         Cx KRM3 KM1 FL0 CMVGRL
2107                 RM8 M3 KGOTO9                   ; R3 (SP) := R8
2108 
2109 ;#FILL
2110 ; FILL           ( a u c −− )                         Заполнить. Записать u байтов c, начиная с адреса a.
2111 ; Записывает u байт c в память начиная с адреса a
2112 ; Заполнение массивов памяти идентичными символами
2113 ; а − адрес первого байта памяти, куда будет засылаться байт c
2114 ; u − число заполняемых байтов
2115 ; Подразумевается, что мы не пересекаем границу памяти программ и данных
2116 LFILL:          .DB     4
2117                 .TEXT   "FILL"                  ; ( куда сколько что −− ) F79
2118                 .DW     LCMOVEG
2119 FILL:           .DW     JFILL
2120 JFILL:          PKRM03 MB                       ; Символ
2121                 RM3 1 + 
2122 JFILL2:         M8 1 + MA 1 + M3
2123                 KRM8 M0 +/− KX<09               ; Счётчик символов
2124                 KRMA 1 EE 4 − KX>=09 1 − M5 RMB
2125 FILL1:          KM5 FL0 FILL1
2126                 KGOTO9
2127 JBLANK:                                         ; Обработчик FILL
2128                 32
2129                 .DB 59H                         ; FX>=0 aka пропустить 1 байт
2130 JERASE:         CX                              ; Обработчик ERASE
2131 JJFILL:         MB RM3 PGOTO JFILL2
2132 
2133 ;#ERASE
2134 ; ERASE          ( a u −− )                           Стереть. Записать u нулей в память, начиная с адреса a.
2135 ; Записывает u нулевых байтов в память, начиная с адреса a.
2136 ; Обнуление массивов.
2137 LERASE:         .DB     5
2138                 .TEXT   "ERASE"                 ; ( где сколько −−) F79
2139                 .DW     LFILL
2140 ERASE:          .DW     JERASE
2141 
2142 ;#BLANK
2143 ; BLANK          ( a u −− )                           Опробелить. Записать u пробелов в память, начиная с адреса a.
2144 ; Записывает n кодов пробела (32) в память, начиная с адреса a.
2145 LBLANK:         .DB     5
2146                 .TEXT   "BLANK"                 ; ( где сколько −−) F83
2147                 .DW     LERASE
2148 BLANK:          .DW     JBLANK
2149 
2150 ;#HOLD
2151 ; HOLD           ( c −− )                             Перенести литеру c на вершину буфера PAD.
2152 ; Вводит в текущую ячейку выходного буфера PAD символ, код которого в стеке
2153 ; Для использования между <# и U#>
2154 LHOLD:          .DB     4
2155                 .TEXT   "HOLD"                  ; ( символ −−) F79
2156                 .DW     LBLANK
2157 HOLD:           .DW     JHOLD
2158 JHOLD:          PPRM rlHLD 1 −
2159                 FX<0 HOLD1
2160                 PPRM rrHLD 1 − PPM rrHLD 255
2161 HOLD1:          PPM rlHLD
2162                 PPRM rrHLD RME ∗ + 1 EE 4 − MA
2163                 PKRM03 KMA
2164 JDROP:          1                               ; Да, это обработчик DROP
2165 NDROP:          RM3 + M3                        ; А сюда сваливается 2DROP
2166                 KGOTO9
2167 
2168 ;#TIB
2169 ; TIB            ( −− a )                             Дать адрес TIB (входного буфера терминала).
2170 ; Внимание, это не переменная!
2171 ; Занести в стек начальный адрес входного буфера терминала (Terminal Input Buffer).
2172 LTIB:           .DB     3
2173                 .TEXT   "TIB"                   ; ( −− адрес_TIB) F83
2174                 .DW     LHOLD
2175 TIB:            .DW SCONP                       ; Входной буфер терминала
2176                 .DB 2aH,0f8H                    ; 11000
2177 
2178 ;#PlusStore
2179 ; +!             ( x a −− )                           Увеличить на x содержимое ячейки с адресом a.
2180 ; Добавляет к содержимому ячейки с адресом a число x
2181 ; Подразумевается, что ячейка не пересекает границ областей памяти
2182 ; Может увеличивать содержимое десятичных регистров, но не регистров функций
2183 LPSTOR:         .DB     2
2184                 .TEXT   "+!"                    ; F79
2185                 .DW     LTIB
2186 PSTOR:          .DW     JPSTOR
2187 JPSTOR:         RM3 MA 1 + M8 1 + M3
2188                 KRMA 1 EE 4 − KX>=09 MA         ; Защита от "переменных" в памяти программ
2189                 1000 − FX>=0 PSTOR1             ; 1000 быстрее, чем 1 ВП 3
2190                 RMA 1 + MB
2191                 KRMA RME ∗ KRMB +               ; @
2192                 KRM8 +                          ; +
2193                 RME FX^2 +                      ; X1 := 65536
2194 PSTOR3:         FANS − FX<0 PSTOR3
2195 PSTOR4:         FANS + FX>=0 PSTOR4
2196 PSTOR2:         ENT RME / KINT KMA
2197                 RME ∗ − KMB  KGOTO9             ; ! EXIT
2198 PSTOR1:         KRMA KRM8 + KMA  KGOTO9
2199 
2200 ;#PlusPlusStore
2201 ; ++!            ( y a −− )                           Косвенная запись y по указателю в a с предварительным автоувеличением (аналог КП4..КП6).
2202 ; 1. Добавляет единицу к содержимому ячейки с адресом a
2203 ; 2. Записывает x по адресу, содержащемуся в этой ячейке (в байтовой области это адрес байта, а не ячейки)
2204 ; Подразумевается, что ячейка не пересекает границ областей памяти.
2205 ; Указатель может располагаться в десятичных регистрах, но не регистрах функций.
2206 LPPSTOR:        .DB     3
2207                 .TEXT   "++!"                   ; ( число адрес −− ) К
2208                 .DW     LPSTOR
2209 PPSTOR:         .DW     JPPSTOR
2210 JPPSTOR:        RM3 MA 1 + M8 1 + M3
2211                 KRMA 1 EE 4 − KX>=09 MA         ; Защита от "указателей" в памяти программ
2212                 1000 − PX>=0 PPSTOR1            ; 1000 быстрее, чем 1 ВП 3
2213                 1                               ; 1+
2214 PPSTOR0:        RMA 1 + MB <−>
2215                 KRMA RME ∗ KRMB +               ; @
2216                 +                               ; 1+ или 1−
2217                 ENT                             ; Сохраним адрес, нам по нему ещё x записывать
2218                 ENT RME / KINT KMA              ; Старший байт
2219                 RME ∗ − KMB                     ; Младший байт
2220                 Cx GOTO PPSTOR2
2221 MMSTOR1:        1 +/−                           ; автодекремент десятичного регистра
2222                 .DB 5cH                         ; FX<0 aka пропустить 1 байт
2223 PPSTOR1:        1                               ; автоинкремент десятичного регистра
2224 PPSTOR1A:       KRMA KINT + KMA
2225                 1
2226 PPSTOR2:        EE 4 − KX>=09 MA                ; Защита от обращения в память программ
2227                 1000 − FX>=0 PPSTOR4            ; Если десятичный регистр, записываем туда любой x
2228                 KRM8 FX<0 PPSTOR5
2229                 RME + KMA KGOTO9                ; Отрицательные числа в байтовые регистры приводятся к 128..255
2230 PPSTOR4:        KRM8
2231 PPSTOR5:        KMA KGOTO9                      ; !
2232 
2233 ;#MinusMinusStore
2234 ; −−!            ( y a −− )                           Косвенная запись y по указателю в a с предварительным автоуменьшением (аналог КП0..КП3).
2235 ; 1. Вычитает единицу из содержимого ячейки с адресом a
2236 ; 2. Записывает x по адресу, содержащемуся в этой ячейке (в байтовой области это адрес байта, а не ячейки)
2237 ; Подразумевается, что ячейка не пересекает границ областей памяти.
2238 ; Указатель может располагаться в десятичных регистрах, но не регистрах функций.
2239 LMMSTOR:        .DB     3
2240                 .TEXT   "−−!"                   ; ( число адрес −− ) К
2241                 .DW     LPPSTOR
2242 MMSTOR:         .DW     JMMSTOR
2243 JMMSTOR:        RM3 MA 1 + M8 1 + M3
2244                 KRMA 1 EE 4 − KX>=09 MA         ; Защита от "указателей" в памяти программ
2245                 1000 − FX>=0 MMSTOR1            ; 1000 быстрее, чем 1 ВП 3
2246                 1 +/−                           ; 1−
2247                 PGOTO PPSTOR0
2248 
2249 ;#TOGGLE
2250 ; TOGGLE         ( a c −− )                           Изменить байт с адресом a по xor−маске c.
2251 ; У TOGGLE стандартный, но необычный порядок операндов. Будьте внимательны!
2252 LTOGL:          .DB     6
2253                 .TEXT   "TOGGLE"                ; fig−Форт, FORTH ИТЭФ
2254                 .DW     LMMSTOR
2255 TOGL:           .DW     JTOGL
2256 JTOGL:          RM3 M8 1 + MA 1 + M3
2257                 KRMA 1 EE 4 − KX>=09            ; Не меняем ничего в памяти программ
2258                 MA KRM8 KRMA KXOR KMA
2259                 KGOTO9
2260 
2261 ;#Fetch
2262 ; @              ( a −− x )                           Извлечь. Считать значение ячейки по адресу a.
2263 ; Замещает адрес в стеке его содержимым, двухбайтовая ячейка содержит число со знаком.
2264 ; Также может считать десятичный регистр, если адрес указывает на него.
2265 ; Не предназначена для считывания регистров функций!
2266 LAT:            .DB     1
2267                 .TEXT   "@"                     ; ( адрес −− целое) F79
2268                 .DW     LTOGL
2269 AT:             .DW     JAT
2270 JAT:            PKRM03
2271 ;AT0:
2272                 MA  1
2273 ;AT00:
2274                 EE 4 −  FX>=0 AT1
2275                 MA M5 1000 − FX>=0 AT2          ; 1000 быстрее, чем 1 ВП 3
2276                 KRMA 128 − FX>=0 UAT0
2277                 RME ∗ KRM5
2278                 GOTO AT01
2279 AT2:            KRMA PKM03 KGOTO9
2280 AT1:            RMA KPRGM 128 − FX>=0 UAT1
2281                 RME ∗ RMA 1 + KPRGM
2282 AT01:           +
2283                 32768 − PKM03 KGOTO9
2284 
2285 ;#UFetch
2286 ; U@             ( a −− U )                           Извлечь беззнаковое. Считать беззнаковое значение ячейки с адресом a.
2287 ; Считываем в стек 2−х байтовое целое без знака.
2288 ; Единый адрес должен указывать на память программ или на байтовые регистры.
2289 ; Не предназначена для считывания регистров функций и десятичных регистров!
2290 ; Без лишних проверок, очень быстро.
2291 LUAT:           .DB     2
2292                 .TEXT   "U@"                    ; ( адрес −− беззнаковое ) К
2293                 .DW     LAT
2294 UAT:            .DW     JUAT
2295 JUAT:           PKRM03 MA  1 EE 4 −  FX>=0 UAT1
2296                 MA M5
2297 UAT0:           KRMA RME ∗ KRM5 + PKM03 KGOTO9  ; Сюда есть переход из @
2298 UAT1:           RMA KPRGM RME ∗                 ; Сюда есть переход из @
2299                 RMA 1 + KPRGM +
2300                 PKM03 KGOTO9
2301 
2302 ;#CFetch
2303 ; C@             ( a −− x )                           Извлечь байт. Считать значение по адресу a.
2304 ; Извлекает байт информации из ячейки, адрес которой находится в стеке.
2305 ; Если читаем байт из памяти программ или байтовой памяти, старшие биты будут равны нулю.
2306 ; Так можно читать значения из регистров функций и десятичных регистров.
2307 LCAT:           .DB     2
2308                 .TEXT   "C@"                    ; ( адрес −− байт ) F79
2309                 .DW     LUAT
2310 CAT:            .DW     JCAT
2311 JCAT:           PKRM03 1 EE 4 − FX>=0 CAT1
2312 CAT0:           MA KRMA PKM03  KGOTO9
2313 CAT1:           PKRM03
2314 CAT2:           KPRGM PKM03  KGOTO9
2315 
2316 ;#PlusPlusFetch
2317 ; ++@            ( a −− x )                           Косвенное чтение по указателю в a с предварительным автоувеличением (аналог КИП4..КИП6).
2318 ; 1. Добавляет единицу к содержимому ячейки с адресом a
2319 ; 2. Кладёт на стек x, расположенное по адресу, содержащемуся в этой ячейке (в байтовой области это адрес байта, а не ячейки)
2320 ; Подразумевается, что ячейка не пересекает границ областей памяти.
2321 ; Указателем может быть десятичный регистр, но не регистр функций.
2322 LPPAT:          .DB     3
2323                 .TEXT   "++@"                   ; ( адрес −− x ) К
2324                 .DW     LCAT
2325 PPAT:           .DW     JPPAT
2326 JPPAT:          PKRM03 1 EE 4 − KX>=09 MA       ; Защита от "указателей" в памяти программ
2327                 1000 − FX>=0 PPAT1
2328                 1                               ; 1+
2329 PPAT0:          RMA 1 + MB <−>
2330                 KRMA RME ∗ KRMB +               ; @
2331                 + ENT                           ; 1+ или 1−
2332                 ENT RME / KINT KMA              ; Старший байт
2333                 RME ∗ − KMB                     ; Младший байт
2334                 Cx GOTO PPAT00
2335 MMAT1:          1 +/−                           ; автодекремент десятичного регистра
2336                 .DB 5cH                         ; FX<0 aka пропустить 1 байт
2337 PPAT1:          1                               ; автоинкремент десятичного регистра
2338 PPAT1A:         KRMA KINT + KMA
2339                 1
2340 PPAT00:         EE 4 − FX<0 CAT0
2341                 FANS + GOTO CAT2
2342 
2343 ;#MinusMinusFetch
2344 ; −−@            ( a −− x )                           Косвенное чтение по указателю в a с предварительным автоуменьшением (аналог КИП0..КИП3).
2345 ; 1. Вычитает единицу из содержимого ячейки с адресом a
2346 ; 2. Кладёт на стек x, расположенное по адресу, содержащемуся в этой ячейке (в байтовой области это адрес байта, а не ячейки)
2347 ; Подразумевается, что ячейка не пересекает границ областей памяти.
2348 ; Указателем может быть десятичный регистр, но не регистр функций.
2349 LMMAT:          .DB     3
2350                 .TEXT   "−−@"                   ; ( адрес −− x ) К
2351                 .DW     LPPAT
2352 MMAT:           .DW     JMMAT
2353 JMMAT:          PKRM03 1 EE 4 − KX>=09 MA       ; Защита от "указателей" в памяти программ
2354                 1000 − PX>=0 MMAT1              ; 1000 быстрее, чем 1 ВП 3
2355                 1 +/−                           ; 1−
2356                 PGOTO PPAT0
2357 
2358 ;#Store
2359 ; !              ( x a −− )                           Присвоить. Записывает x в ячейку с адресом a.
2360 ; Записывает число x в ячейку с адресом a.
2361 ; Может записать число в десятичный регистр, но не в регистр функций.
2362 LSTORE:         .DB     1
2363                 .TEXT   "!"                     ; ( значение адрес −− ) F79
2364                 .DW     LMMAT
2365 STORE:          .DW     JSTORE
2366 JSTORE:         RM3 M8 1 + MA 1 + M3
2367                 KRM8 1 EE 4 − KX>=09            ; Защита от записи в память программ
2368                 M8 M5 1000 − FX>=0 STORE1       ; 1000 быстрее, чем 1 ВП 3
2369                 KRMA FX<0 STORE2
2370                 65536 +                         ; При записи отрицательных чисел в двоичную память
2371 STORE2:         ENT RME / KINT KM8              ; Запишем старший байт
2372                 RME ∗ − KM5                     ; Потом младший байт
2373                 KGOTO9
2374 STORE1:         KRMA KM8 KGOTO9                 ; Запись в десятичный регистр
2375 
2376 ;#ZeroStore
2377 ; 0!             ( a −− )                             Обнулить. Записывает ноль в ячейку с адресом a.
2378 ; Записывает число 0 в ячейку с адресом a.
2379 ; Может обнулять десятичные регистры, но не регистры функций.
2380 LZSTORE:        .DB     2
2381                 .TEXT   "0!"                    ; ( адрес −− )
2382                 .DW     LSTORE
2383 ZSTORE:         .DW     JZSTORE
2384 JZSTORE:        RM3 M8 1 + M3
2385                 KRM8 1 EE 4 − KX>=09            ; Защита от записи в память программ
2386                 M8 M5 1000 − FX>=0 ZSTORE1      ; 1000 быстрее, чем 1 ВП 3
2387                 Cx KM8 KM5 KGOTO9               ; Обнуление двоичной ячейки
2388 ZSTORE1:        Cx KM8 KGOTO9                   ; Обнуление десятичного регистра
2389 
2390 ;#CStore
2391 ; C!             ( x a −− )                           Записывает байт x по адресу a.
2392 ; Может также записать число в десятичный регистр, но не регистр функций.
2393 ; +++ Форт−79 требует, чтобы записывались только младшие 8 бит числа в стеке −− проверить.
2394 LCSTOR:         .DB     2
2395                 .TEXT   "C!"                    ; ( байт адрес −− ) F79
2396                 .DW     LZSTORE
2397 CSTOR:          .DW     JCSTOR
2398 JCSTOR:         RM3 M8 1 + MA 1 + M3
2399                 KRM8 1 EE 4 − KX>=09            ; Защита от записи в память программ
2400                 M8 1000 − PX<0 STORE1           ; 1000 быстрее, чем 1 ВП 3
2401                 KRMA FX<0 CSTOR2
2402                 RME +
2403 CSTOR2:         KM8 KGOTO9
2404 
2405 ;#BODYfrom
2406 ; BODY>          ( a −− т )                           От тела. От поля параметров к полю кода.
2407 ; Слово из расширения стандарта Форт−83 (не обязательное).
2408 LCFA:           .DB     5
2409                 .TEXT   "BODY>"                 ; ( pfa −− cfa ) F83
2410                 .DW     LCSTOR
2411 CFA:            .DW     JTWOM                   ; В FORTH ИТЭФ использовалось, как 2−
2412 
2413 ;#StoreCSP
2414 ; !CSP           ( −− )                               Запомнить адрес вершины стека в CSP.
2415 LSCSP:          .DB     4
2416                 .TEXT   "!CSP"                  ; fig−Форт, ФОРТ−ЕС, FORTH ИТЭФ
2417                 .DW     LCFA
2418 SCSP:           .DW     JSCSP
2419 JSCSP:          Cx EE 4 RM3 +
2420 ;               ENT RME / FANS <−> KINT PPM rrCSP ∗ − PPM rlCSP
2421                 ENT RME / KINT PPM rrCSP RME ∗ − PPM rlCSP      ; 13
2422                 KGOTO9
2423 
2424 ;#HERE
2425 ; HERE           ( −− a )                             Здесь. Дать адрес текущей вершины словаря.
2426 ; Выдаёт адрес первой свободной ячейки в словаре.
2427 LHERE:          .DB     4
2428                 .TEXT   "HERE"                  ; F79
2429                 .DW     LSCSP
2430 HERE:           .DW     JHERE
2431 JHERE:          PPRM rrDP RME ∗ PPRM rlDP + KM3
2432                 KGOTO9
2433 
2434 ;#ALLOT
2435 ; ALLOT          ( D −− )                             Занять. Сместить вершину словаря на n байт.
2436 ; Добавляет D байт к полю параметров слова, описанного последним.
2437 ; +++ Проверить размер и эффективность высокоуровневого определения.
2438 LALLOT:         .DB     5
2439                 .TEXT   "ALLOT"                 ; ( n −− ) F79
2440                 .DW     LHERE
2441 ALLOT:          .DW     JALLOT
2442 JALLOT:         RM3 MA 1 + M3
2443 
2444                 PPRM rlDP KRMA +                                        ;  5
2445                 ENT RME / FANS <−> KINT MA ∗ − PPM rlDP                 ; 12
2446                 RMA KX!=09                                              ;  2
2447                 PPRM rrDP + PPM rrDP                                    ;  7
2448 
2449 ;               PPRM rrDP RME ∗ PPRM rlDP + KRMA +                      ; 11
2450 ;               ENT RME / FANS <−> KINT PPM rrDP ∗ − PPM rlDP           ; 14
2451                 KGOTO9
2452 
2453 ;#deHERE
2454 ; ДHERE          ( −− a )                             Дать адрес текущей вершины десятичного словаря.
2455 ; Выдаёт адрес первого свободного десятичного регистра в десятичном словаре.
2456 LDHERE:         .DB     5,132
2457                 .TEXT   "HERE"                  ; "ДHERE" К
2458                 .DW     LALLOT
2459 DHERE:          .DW     JDHERE
2460 JDHERE:         PPRM rrDDP RME ∗ PPRM rlDDP + KM3
2461                 KGOTO9
2462 
2463 ;#deALLOT
2464 ; ДALLOT         ( D −− )                             Сместить вершину десятичного словаря на D регистров.
2465 ; Резервирует D десятичных регистров в десятичном словаре.
2466 ; +++ Проверить размер и эффективность высокоуровневого определения.
2467 LDALLOT:        .DB     6,132
2468                 .TEXT   "ALLOT"                 ; "ДALLOT" ( n −− ) К
2469                 .DW     LDHERE
2470 DALLOT:         .DW     JDALLOT
2471 JDALLOT:        RM3 MA 1 + M3
2472 
2473                 PPRM rlDDP KRMA +                                       ;  5
2474                 ENT RME / FANS <−> KINT MA ∗ − PPM rlDDP                ; 12
2475                 RMA KX!=09                                              ;  2
2476                 PPRM rrDDP + PPM rrDDP                                  ;  7
2477 
2478 ;               PPRM rrDDP RME ∗ PPRM rlDDP + KRMA +
2479 ;               ENT RME / FANS <−> KINT PPM rrDDP ∗ − PPM rlDDP
2480                 KGOTO9
2481 
2482 ;#Bracket
2483 ; [              ( −− )                               Установить состояние исполнения.
2484 ; Переключить текстовый интерпретатор в состояние исполнения
2485 LLBRAC:         .DB     81H
2486                 .TEXT   "["                     ; F79
2487                 .DW     LDALLOT
2488 LBRAC:          .DW     JLBRAC
2489 JLBRAC:         CX                              ; Прекращение компиляции, начало исполнения (обнуление STATE)
2490 SETSTATE:       PPM rlSTATE
2491                 CX
2492                 PPM rrSTATE
2493                 KGOTO9
2494 ;               .DW STATE,ZSTORE, EXIT          ; 6 байт шитого кода vs 9 байт ЯМК
2495 
2496 ;#right−bracket
2497 ; ]              ( −− )                               Установить состояние компиляции.
2498 ; Переключить текстовый интерпретатор в состояние компиляции.
2499 LRBRAC:         .DB     1
2500                 .TEXT   "]"                     ; F79
2501                 .DW     LLBRAC
2502 RBRAC:          .DW     JRBRAC
2503 JRBRAC:         128                             ; Начало компиляции
2504                 PGOTO SETSTATE
2505 ;               .DW LITB
2506 ;               .DB 128
2507 ;               .DW STATE,STORE, EXIT           ; 9 байт шитого кода vs 6 байт ЯМК
2508 
2509 ;#HEX
2510 ; HEX            ( −− )                               Установить шестнадцатеричную систему счисления.
2511 ; Устанавливает шестнадцатеричную систему счисления для ввода−вывода.
2512 LHEX:           .DB     3
2513                 .TEXT   "HEX"                   ; F79
2514                 .DW     LRBRAC
2515 HEX:            .DW     JHEX
2516 JHEX:           16
2517 SETBASE:        PPM rlBASE
2518                 CX PPM rrBASE
2519                 KGOTO9
2520 ;               .DW LIT,16, BASE,STORE, EXIT    ; 9−10 байт шитого кода vs 10 байт ЯМК
2521 
2522 ;#DECIMAL
2523 ; DECIMAL        ( −− )                               Установить десятичную систему счисления.
2524 ; Устанавливает десятичную систему счисления для ввода−вывода.
2525 LDEC:           .DB     7
2526                 .TEXT   "DECIMAL"               ; F79
2527                 .DW     LHEX
2528 DEC:            .DW     JDEC
2529 JDEC:           10
2530                 PGOTO SETBASE
2531 ;               .DW LIT,10, BASE,STORE, EXIT    ; 9−10 байт шитого кода vs 5 байт ЯМК
2532 
2533 ;#MinusTRAILING
2534 ; −TRAILING      ( a u1 −− a u2 )                     Отсечь конечные пробелы.
2535 ; Преобразует число символов u1 в строке, начинающейся с адреса a, в число u2, не включающее
2536 ; число пробелов, которые имеются в конце строки. Адрес a остаётся неизменным.
2537 ; Можно попробовать пропускать табуляции, ВК и ПС
2538 ; Мы считаем, что дело происходит в области данных.
2539 LDTRAI:         .DB     9
2540                 .TEXT   "−TRAILING"             ; F79
2541                 .DW     LDEC
2542 DTRAI:          .DW     JDTRAI
2543 JDTRAI:         RM3 M8 M5 KRM5
2544                 1 EE 4 − KX>=09
2545                 KRM8 M0 +/− KX<09
2546                 − M1
2547 DTRAI1:         KRM1 32 −
2548                 FX=0 DTRAI2
2549                 FL0 DTRAI1
2550                 CX M0
2551 DTRAI2:         RM0 KM8
2552                 KGOTO9
2553 
2554 ;#UPPER
2555 ; UPPER          ( a u −− )                           Перевести в верхний регистр u литер по адресу a.
2556 ; Перевести строку символов в верхний регистр.
2557 LUPPER:         .DB     5
2558                 .TEXT   "UPPER"                 ; Форт ИТЭФ
2559                 .DW     LDTRAI
2560 UPPER:          .DW     JUPPER
2561 JUPPER:         RM3 M8 1 + MA 1 + M3
2562                 KRM8 M0 +/− KX<09
2563                 KRMA 10001 − KX>=09 M5
2564 UPPERL:         KRM5
2565                 97 − PX>=0 UPPER2               ; 'a'
2566                 26 − PX>=0 UPPER3               ; 'z'−96
2567                 37 − PX>=0 UPPER2               ; 'а'−123
2568                 16 − PX>=0 UPPER4               ; 'п'−159
2569                 48 − PX>=0 UPPER2               ; 'р'−176
2570                 16 − FX>=0 UPPER6               ; 'я'−223
2571                 1 − PX=0 UPPER2                 ; 'ё'−241
2572                 240  PGOTO UPPER5               ; 'Ё'
2573 UPPER6:         160  GOTO UPPER7                ; 'Р'+16
2574 UPPER4:         144                             ; 'А'+16
2575                 .DB 0f9H                        ; PX>=0 aka PGOTO UPPER7, пропустить 2 шага
2576 UPPER3:         91                              ; 'A'+26
2577 UPPER7:         +
2578 UPPER5:         PKM05
2579 UPPER2:         PFL0    UPPERL
2580 UPPERR:         KGOTO9
2581 
2582 ;#DEFINITIONS
2583 ; DEFINITIONS    ( −− )                               Делает контекстный набор слов текущим. Новые слова добавляются в него.
2584 ; Это старинный способ менять значение переменной CURRENT.
2585 LDEFIN:         .DB     11
2586                 .TEXT   "DEFINITIONS"                   ; F79
2587                 .DW     LUPPER
2588 DEFIN:          .DW CALL, CONT,UAT, CURR,STORE, EXIT    ; 10 байт шитого кода vs 13 байт ЯМК
2589 
2590 ;#UPDATE
2591 ; UPDATE         ( −− )                               Отметить блочный буфер, как изменённый.
2592 ; Устанавливает в буфере флаг "спасения".
2593 LUPDAT:         .DB     6
2594                 .TEXT   "UPDATE"                ; ( −−) F79
2595                 .DW     LDEFIN
2596 UPDAT:          .DW     JUPDAT
2597 JUPDAT:         128  PPM rrBUFBLK
2598                 KGOTO9
2599 
2600 ;#BUFBLK
2601 ; BUFBLK         ( −− a )                             Переменная. Номер блока, загруженного в буфер.
2602 ; Слово BUFBLK разработано на основе слова X из FORTH ИТЭФ.
2603 LX:             .DB     6
2604                 .TEXT   "BUFBLK"                        ; ( −− +n ) К
2605                 .DW     LUPDAT
2606 BUFN:           .DW SCONP
2607                 .DB 3aH,0f6H                    ; 15094 rrBUFBLK
2608 
2609 ;               ∗∗ Управляющие слова ∗∗
2610 ;
2611 ;#Colon
2612 ; :              ( −− )                               Начать определение слова через двоеточие.
2613 ; Начиная с Форта−94 двоеточие не влияет на CONTEXT
2614 LCOLON:         .DB     1
2615                 .TEXT   ":"                     ; ( "<пр>имя" −− ) F94
2616                 .DW     LX
2617 COLON:          .DW CALL,QEXEC,SCSP,CREAT,SMUG  ; Раньше было: CURR,UAT,CONT,STORE
2618                 .DW RBRAC,PSCOD
2619 CALLD:                                          ;∗∗ CALL, когда управление передаётся в память данных
2620                 KGSBC                           ; RPUSH RI ; RX := W, адрес поля кода нового слова
2621                 1 +                             ; RX := PFA−1, передаём в указатель шитого кода R6=RI−1
2622 SETRIDAT:
2623                 M6                              ; RI := RX
2624 SETDAT:         .NUMT RPUSHRID
2625                 MC                              ; RC := RPUSHRID        ;∗∗ Следующий вызов −− из памяти данных
2626                 .NUM NEXTD
2627                 M9                              ; R9 := NEXTD           ;∗∗
2628 NEXTD:                                          ; NEXT для шитого кода из памяти данных.
2629                 KRM6 RME ∗ KRM6 + M7            ; W := MEMW[RI++] считываем шитый код
2630                 1 EE 4 − PX>=0 NEXTPP           ; Слово из памяти программ?
2631 NEXTDD:         M7 M5 KRM7 RME ∗ KRM5 +         ; Считываем MEMW[W], это должно быть CFA очередного слова в шитом коде
2632                 M8 KGOTO8                       ; передаём управление на адрес, записанный в CFA
2633 ;
2634 RETURN:                                         ; Обработчик EXIT
2635                 RM2 1 − M5 3 + M2
2636                 KRM5 RME ∗ KRM5 +
2637                 PPM 9042                        ; Регистр, увы, мучаем в любом случае
2638                 10001 −
2639                 PX<0 SETRIDAT
2640                 GOTO SETPRG
2641 ;
2642 CALL:                                           ;∗∗ CALL, когда управление передаётся в память программ
2643                 KGSBC                           ; RPUSH RI ; RX := W, адрес поля кода нового слова
2644                 2 +                             ; Теперь RX указывает на его тело
2645 SETRIPRG:
2646                 PPM 9042                        ; R9042 := RX
2647 SETPRG:         .NUMT RPUSHRIP
2648                 MC                              ; RC := RPUSHRIP        ;∗∗ Следующий вызов −− из памяти программ
2649                 .NUM NEXTP
2650                 M9                              ; R9 := NEXTP           ;∗∗
2651 NEXTP:                                          ; NEXT для шитого кода из памяти программ.
2652                 PPRM 9044 RME ∗ PPRM 9044 +
2653 DOEXECRX:       M7                              ; W := MEMW[RI++] считываем шитый код
2654                 1 EE 4 − FX<0 NEXTDD            ; Слово из области двоичных данных?
2655 NEXTPP:         RM7 KPRGM RME ∗ RM7 1 + KPRGM + ; Считываем MEMW[W], это должно быть CFA очередного слова в шитом коде
2656                 M8 KGOTO8                       ; передаём управление на адрес, записанный в CFA
2657 
2658 JEXEC:                                          ; Обработчик EXECUTE
2659                 RM3 M8 1 + M3 KRM8              ; POP RX
2660                 GOTO DOEXECRX                   ; W := RX, JMP MEMW[W]
2661 
2662 ;#Semi
2663 ; ;              ( −− )                               Закончить определение через двоеточие.
2664 ; Форт−79 требует выводить сообщение об ошибке, если после чтения с диска мы всё ещё не встретили ;
2665 LSMI:           .DB     81H
2666                 .DB     3BH                     ; ";" F79
2667                 .DW     LCOLON
2668 SMI:            .DW CALL, QCSP, COMP,EXIT, SMUG, LBRAC, EXIT
2669 
2670 ;#CONSTANT
2671 ; CONSTANT       ( D −− )                             Определить следующее слово, как константу со значением D.
2672 ; Константы в памяти двоичных данных всегда 16−битные со знаком.
2673 ; Мы избавились от констант в памяти программ, реализовав их быстрыми примитивами.
2674 LCON:           .DB     8
2675                 .TEXT   "CONSTANT"              ; ( x "<пр>имя" −− ) F79
2676                 .DW     LSMI
2677 CON:            .DW CALL
2678 RCON:           .DW CREAT, COMMA, PSCOD
2679 SCOND:          RM7 1 + M5                      ; ( −− x ) из памяти данных
2680                 KRM5 RME ∗ KRM5
2681                 PGOTO PLUSKM3M                  ; Можно раскрыть, ускорить за счёт места в памяти программ
2682 SCONP:          RM7 3 + KPRGM                   ; ( −− x ) в памяти программ всегда беззнаковые
2683                 FANS 1 − KPRGM RME ∗ + KM3 KGOTO9
2684 
2685 ;#VARIABLE
2686 ; VARIABLE       ( −− )                               Определить следующее слово, как переменную с начальным значением 0.
2687 ; Каллисто инициализирует новую переменную нулём.
2688 LVAR:           .DB     8
2689                 .TEXT   "VARIABLE"              ; ( "<пр>имя" −− ) F79 F83 F12
2690                 .DW     LCON
2691 VAR:            .DW CALL, CREAT,ZERO,COMMA,EXIT ; Действие по умолчание от CREATE нам вполне подходит.
2692 
2693 ;#FVARIABLE
2694 ; FVARIABLE      ( −− )                               Определить следующее слово, как десятичную переменную с начальным значением 0.
2695 ; Каллисто инициализирует новую переменную нулём.
2696 LFVAR:          .DB     9
2697                 .TEXT   "FVARIABLE"             ; ( "<пр>имя" −− )
2698                 .DW     LVAR
2699 FVAR:           .DW CALL, DHERE,ZERO,DCOMMA,BRAN,RCON   ; ДHERE 0 Д, CONSTANT
2700 
2701 ;#VALUE
2702 ; VALUE          ( x −− )                             Определить следующее слово, как десятичную величину с начальным значением x.
2703 ; Используем десятичные регистры для хранения величин VALUE
2704 ; Данные хранятся в ячейке между двумя CFA, чтобы программист мог создавать собственные
2705 ; порождающие слова с двумя обработчиками высокого уровня, использущие стандартное TO
2706 LVALUE:         .DB     5
2707                 .TEXT   "VALUE"                 ; ( x "<пр>имя" −− ) F12
2708                 .DW     LFVAR
2709 VALUE:          .DW CALL, CREAT, DHERE, LITP
2710                 .DB 39,16                       ; 10000
2711                 .DW SUB, COMMA, DCOMMA, LITP,TOVALUE,COMMA, PSCOD
2712 ; Исполняющая часть VALUE (обычное вхождение величины)
2713                 RM7 1 + M5                      ; ( −− x ) из десятичного регистра
2714                 KRM5 RME ∗ KRM5 + M7            ; Получаем номер регистра
2715                 KRM7 KM3 KGOTO9                 ; Записываем значение в стек, NEXT
2716 TOVALUE:        RM7 3 − M5                      ; ( x −− ) в десятичный регистр
2717                 KRM5 RME ∗ KRM5 + M7            ; Получаем номер регистра
2718                 RM3 MA 1 + M3                   ; RA := адрес значения
2719                 KRMA KM7 KGOTO9                 ; Записываем значение в десятичный регистр, NEXT
2720 
2721 ;#DEFER
2722 ; DEFER          ( −− )                               Определить следующее слово, как действие с начальным значением НОП (нет операции).
2723 ; Используем поле данных DEFER для хранения xt (в виде адреса в единой адресации)
2724 ; Соглашение по векторному поля кода такое же, как у VALUE −− ведь мы используем одно и то же слово TO
2725 LDEFER:         .DB     5
2726                 .TEXT   "DEFER"                 ; ( "<пр>имя" −− )
2727                 .DW     LVALUE
2728 DEFER:          .DW CALL, CREAT,LITP,SNOP,COMMA ; Инициализируем DEFER токеном НОП
2729                 .DW LITP,TODEFER,COMMA, PSCOD   ; TODEFER −− второй элемент векторного поля кода
2730 ; Исполняющая часть DEFER (обычное вхождение слова)
2731                 RM7 M5 KRM5                     ; ( −− ) выполнить сохранённый xt
2732                 KRM5 RME ∗ KRM5 +               ; RX := сохранённый xt
2733                 PGOTO DOEXECRX                  ; W := RX, JMP MEMW[W]
2734 TODEFER:
2735                 RM7 3 − M5                      ; R5 готов к косвенной адресации с предв. автоинкрементом
2736                 RM3 MA 1 + M3                   ; RA содержит номер регистра, в котором хранится новый xt
2737                 KRMA                            ; Получаем новый CFA
2738                 ENT RME / FANS <−> KINT KM5     ; Сохраняем старший байт нового CFA
2739                 ∗ − KM5 KGOTO9                  ; Сохраняем младший байт нового CFA, NEXT
2740 
2741 ;#USER
2742 ; USER           ( −− a )                             Обработчик переменных типа USER (слово без заголовка).
2743 ; Переменные USER могут быть только в памяти программ и их поле параметров занимает всего 1 байт.
2744 ; Поэтому определяющая часть слова некорректна и излишня
2745 ; +++ Точно 12 переменных USER ? А если их в CONSTANT ?
2746 ;LUSER:         .DB     4
2747 ;               .TEXT   "USER"                  ; F79
2748 ;               .DW     LDEFER
2749 ;USER:          .DW CALL, CON, PSCOD
2750 SUSE:           RM7 2 + KPRGM 11000 + KM3 KGOTO9        ; 000 быстрее, чем ВП 3
2751 
2752 ;#DOES
2753 ; DOES>          ( −− a )                             Начало обработчика в порождающем слове.
2754 ; Все слова, определённые через DOES> находятся в памяти данных
2755 ; DOES> переводится с Форта на русский как "исполняется".
2756 ; Слово присутствует во всех фортах, но начиная с Форта−83 его реализация сильно отличается из−за отсутствия в этих языках слова <BUILDS
2757 LDOES:          .DB     5
2758                 .TEXT   "DOES>"                 ; ( −− pfa) F79
2759                 .DW     LDEFER
2760 DOESD:          .DW CALL, FROMR, LITP
2761                 .DB 39,16                       ; 10000
2762                 .DW SUB, LATES,N2PFA,STORE, PSCOD
2763                 ;∗∗ CALL по адресу из @PFA с засылкой в стек PFA+2
2764 SDOED:                                          ; Обработчик в памяти данных, обычный случай
2765                 KGSBC                           ; RPUSH RI; RX := W
2766                 2 + M7 M5                       ; W указывало на SDOED, а теперь указывает на xt (CFA) обработчика
2767                 10002 + KM3                     ; PUSH W+2      ; PFA+2 с трансляцией адресов
2768                 KRM7 RME ∗ KRM5 +               ; CFA всегда считывается из памяти данных
2769                 1 −                             ; RX := PFA−1, передаём в указатель шитого кода R6=RI
2770                 PGOTO SETRIDAT                  ; Переходим на NEXT для области данных
2771 
2772 DOESP:          .DW     CALL                    ; Сейчас используется только для слова VOCABULARY
2773                 .DW FROMR,LATES,N2PFA,STORE,PSCOD
2774 SDOEP:                                          ; Обработчик в памяти программ, единичный случай
2775                 KGSBC                           ; RPUSH RI; RX := W
2776                 2 + M7 M5                       ; W указывало на SDOE, а теперь на xt (CFA) обработчика
2777                 10002 + KM3                     ; PUSH W+2      ; PFA+2 с трансляцией адресов
2778                 KRM7 RME ∗ KRM5 +               ; .NUM DOVOC
2779                 PGOTO SETRIPRG                  ; Переходим на NEXT для области программ
2780 
2781 ;#FnumS
2782 ; F#S            ( x −− )                             Преобразовать десятичное число.
2783 ; Преобразовывает число с плавающей запятой в строку символов.
2784 LDIGF:          .DB     3
2785                 .TEXT   "F#S"                   ; ( r −− ) К
2786                 .DW     LDOES
2787 DIGF:           .DW     JDIGF
2788 JDIGF:          CX PPM 9030                     ; Программирование индексного регистра 0
2789                 PPM 9031                        ; Программирование индексного регистра 1
2790                 PKRM03 PPM 9035                 ; Преобразование числа с естественной или плавающей запятой в строку символов
2791                 RM3 1 + M3
2792                 PPRM rrHLD RME ∗ PPRM rlHLD +   ; HLD
2793                 10001 M0 − M5
2794                 .DB 0f9H                        ; PX>=0 aka PGOTO ED1 aka пропустить 2 байта
2795 ED0:            FANS KM5
2796 ED1:            PPRM 9034 KNOT FX=0 ED0         ; Чтение данных по индексному регистру 1 с автоинкриментом
2797                 RM5 RM0 +
2798                 ENT RME / FANS <−> KINT PPM rrHLD ∗ − PPM rlHLD
2799                 KGOTO9
2800 
2801 ;               ∗∗ Константы ∗∗
2802 ;
2803 ;#FALSE
2804 ; FALSE          ( −− 0 )                             Ложь. Число 0.
2805 LZERO:          .DB     5
2806                 .TEXT   "FALSE"
2807                 .DW     LDIGF
2808 ZERO:           .DW     JZERO
2809 
2810 ;#One
2811 ; 1              ( −− 1 )                             Число 1.
2812 LONE:           .DB     1
2813                 .TEXT   "1"
2814                 .DW     LZERO
2815 ONE:            .DW     JONE
2816 
2817 ;#Two
2818 ; 2              ( −− 2 )                             Число 2.
2819 LTWO:           .DB     1
2820                 .TEXT   "2"
2821                 .DW     LONE
2822 TWO:            .DW     JTWO
2823 
2824 ;#TRUE
2825 ; TRUE           ( −− −1 )                            Истина. Число −1.
2826 LTRUE:          .DB     4
2827                 .TEXT   "TRUE"
2828                 .DW     LTWO
2829 TRUE:           .DW     JTRUE
2830 JTRUE:          1 +/− KM3 KGOTO9
2831 
2832 ;#Pi
2833 ; &pi;              ( −− 3,14159265359 )                 Число пи.
2834 ; Число пи.
2835 LPI:            .DB     1,185                           ; ( −− r ) К
2836                 .DW     LTRUE
2837 PI:             .DW     JPI
2838 JPI:            FPI KM3  KGOTO9
2839 
2840 ;#BL
2841 ; BL             ( −− 32 )                            Пробел. Число 32.
2842 LBL:            .DB     2
2843                 .TEXT   "BL"                            ; ( −− 32) F79
2844                 .DW     LPI
2845 BL:             .DW     JBL
2846 JBL:            3
2847 JTWO:           2 KM3 KGOTO9                            ; Обработчик 2
2848 
2849 ;#CDivL
2850 ; C/L            ( −− 64 )                            Длина строки. Число 64.
2851 ; Количество символов в одной строке экрана Форта.
2852 LCL0:           .DB     3
2853                 .TEXT   "C/L"                           ; ( −− 64)
2854                 .DW     LBL
2855 LIT64:          .DW SCONP,64                            ; CHAR# PER LINE, осторожней, иногда исп. как 64
2856  
2857 ;#BDivBUF
2858 ; B/BUF          ( −− 3072 )                          Размер буфера в байтах. Число 3072.
2859 ; Вообще−то в Форте−79 зафиксирован размер блока 1024 байт. Но у нас блок побольше будет!
2860 ; Многие древние программы для Форта ожидают, что эта константа равна 1024 и будут удивлены.
2861 ; Если это слово используется в чужой программе, её работоспособность на Каллисто надо проверять тщательней.
2862 LB3BUF:         .DB     5
2863                 .TEXT   "B/BUF"                         ; ( −− 3072) F79
2864                 .DW     LCL0
2865 B3BUF:          .DW SCONP, 3072
2866 
2867 ;#RT
2868 ; RT             ( −− a )                             Десятичная переменная, куда сохраняется регистр T стека МК−161.
2869 LRT:            .DB     2
2870                 .TEXT   "RT"
2871                 .DW     LB3BUF
2872 RT:             .DW SCONP
2873                 .DB 27H,1fH                             ; 10015 rRT
2874 
2875 ;#RZ
2876 ; RZ             ( −− a )                             Десятичная переменная, куда сохраняется регистр Z стека МК−161.
2877 LRZ:            .DB     2
2878                 .TEXT   "RZ"
2879                 .DW     LRT
2880 RZ:             .DW SCONP
2881                 .DB 27H,20H                             ; 10016 rRZ
2882 
2883 ;#RY
2884 ; RY             ( −− a )                             Десятичная переменная, куда сохраняется регистр Y стека МК−161.
2885 LRY:            .DB     2
2886                 .TEXT   "RY"
2887                 .DW     LRZ
2888 RY:             .DW SCONP
2889                 .DB 27H,21H                             ; 10017 rRY
2890 
2891 ;#RX
2892 ; RX             ( −− a )                             Десятичная переменная, куда сохраняется регистр X стека МК−161.
2893 LRX:            .DB     2
2894                 .TEXT   "RX"
2895                 .DW     LRY
2896 RX:             .DW SCONP
2897                 .DB 27H,22H                             ; 10018 rRX
2898 
2899 ;#ATRStore
2900 ; ATR!           ( c −− )                             Установить атрибут вывода на индикатор c (0..7).
2901 ; См. также LIGHT DARK
2902 LATR:           .DB     4
2903                 .TEXT   "ATR!"                          ; ( −− 19001) К
2904                 .DW     LRX
2905 ATR:            .DW JATR
2906 JATR:           PKRM03 PPM9001                          ; R9001  Программирование атрибутов вывода.
2907                 PGOTO JDROP                             ; 1 RM3 + M3 KGOTO9
2908 
2909 ;               ∗∗ USER−переменные ∗∗
2910 ;
2911 ;#SZero
2912 ; S0             ( −− a )                             Переменная, адрес дна стека данных.
2913 LSZERO:         .DB     2
2914                 .TEXT   "S0"                            ; F83
2915                 .DW     LATR
2916 SZERO:          .DW     SUSE
2917                 .DB pS0
2918 
2919 ;#RZero
2920 ; R0             ( −− a )                             Переменная, адрес дна стека возвратов.
2921 LRZERO:         .DB     2
2922                 .TEXT   "R0"
2923                 .DW     LSZERO
2924 RZERO:          .DW     SUSE
2925                 .DB pR0
2926 
2927 ;#deH
2928 ; ДH             ( −− a )                             Переменная, адрес вершины десятичного словаря.
2929 LDDP:           .DB     2,132
2930                 .TEXT   "H"                             ; "ДH" К
2931                 .DW     LRZERO
2932 DDP:            .DW     SUSE
2933                 .DB pDDP
2934 
2935 ;#H
2936 ; H              ( −− a )                             Переменная, адрес вершины словаря.
2937 LDP:            .DB     1
2938                 .TEXT   "H"
2939                 .DW     LDDP
2940 DP:             .DW     SUSE
2941                 .DB pDP
2942 
2943 ;#VOC−LINK
2944 ; VOC−LINK       ( −− a )                             Переменная связи, начало списка из наборов слов.
2945 LVOCLINK:       .DB     8
2946                 .TEXT   "VOC−LINK"
2947                 .DW     LDP
2948 VOCLINK:        .DW     SUSE
2949                 .DB pVOCLINK
2950 
2951 ;#APP
2952 ; APP            ( −− a )                             Переменная, токен запускающего слова.
2953 ; Слово, xt которого хранится в переменной APP, будет выполнено после загрузки сохранения по LOAD"
2954 ; COLD записывает сюда адрес QUIT
2955 LAUTOEXEC:      .DB     3
2956                 .TEXT   "APP"                           ; К
2957                 .DW     LVOCLINK
2958 AUTOEXEC:       .DW     SUSE
2959                 .DB pAUTOEXEC
2960 
2961 ;#KBDFLG
2962 ; KBDFLG         ( −− a )                             Переменная, двухбайтовый флаг клавиатуры.
2963 LKBDF:          .DB     6
2964                 .TEXT   "KBDFLG"                        ; К Флаги клавиатуры: заглавные/строчные, цифры/буквы
2965                 .DW     LAUTOEXEC
2966 KBDF:           .DW     SUSE
2967                 .DB pKBDFLG
2968 
2969 ;#lowFONT
2970 ; _FONT          ( −− a )                             Адрес структуры терминала из 5 байт, описывающей активный шрифт.
2971 LFONT:          .DB     5
2972                 .TEXT   "_FONT"                         ; К
2973                 .DW     LKBDF
2974 FONT:           .DW     SUSE
2975                 .DB pFONT
2976 
2977 ;#BLK
2978 ; BLK            ( −− a )                             Переменная, номер интерпретируемого блока.
2979 ; Переменная BLK переключает ввод информации на терминальный буфер ввода (BLK=0)
2980 ; или на экранный (BLK, равный номеру экрана).
2981 LBLK:           .DB     3
2982                 .TEXT   "BLK"                           ; F79
2983                 .DW     LFONT
2984 BLK:            .DW     SUSE
2985                 .DB pBLK
2986 
2987 ;#toIN
2988 ; >IN            ( −− a )                             Переменная, внутренний указатель входного буфера (0..3071).
2989 ; Смещение внутри терминального или экранного буфера в зависимости от значения BLK
2990 LIN:            .DB     3
2991                 .TEXT   ">IN"                           ; F79
2992                 .DW     LBLK
2993 IN:             .DW     SUSE
2994                 .DB pIN
2995 
2996 ;#SCR
2997 ; SCR            ( −− a )                             Переменная, номер редактируемого экрана.
2998 ; Слово LIST записывает сюда номер последнего отображённого экрана.
2999 LSCR:           .DB     3
3000                 .TEXT   "SCR"                           ; F79
3001                 .DW     LIN
3002 SCR:            .DW     SUSE
3003                 .DB pSCR
3004 
3005 ;#CONTEXT
3006 ; CONTEXT        ( −− a )                             Переменная, контекстный список слов.
3007 ; Переменная указывает на список слов VOCABULARY, в котором происходит поиск слов во время интерпретации входного потока.
3008 LCONTEXT:       .DB     7
3009                 .TEXT   "CONTEXT"                       ; F79
3010                 .DW     LSCR
3011 CONT:           .DW     SUSE
3012                 .DB pCONTEXT
3013 
3014 ;#CURRENT
3015 ; CURRENT        ( −− a )                             Переменная, текущий пополняемый список слов.
3016 ; Переменная указывает список слов VOCABULARY, в который вводятся определения новых слов.
3017 LCURRENT:       .DB     7
3018                 .TEXT   "CURRENT"                       ; F79
3019                 .DW     LCONTEXT
3020 CURR:           .DW     SUSE
3021                 .DB pCURRENT
3022 
3023 ;#STATE
3024 ; STATE          ( −− a )                             Переменная, состояние текстового интерпретатора.
3025 LSTATE:         .DB     5
3026                 .TEXT   "STATE"                         ; F79 F83
3027                 .DW     LCURRENT
3028 STATE:          .DW     SUSE
3029                 .DB pSTATE                              ; 0 => Исполнение
3030 
3031 ;#BASE
3032 ; BASE           ( −− a )                             Переменная, основание системы счисления для ввода−вывода чисел.
3033 LBASE:          .DB     4
3034                 .TEXT   "BASE"                          ; F79
3035                 .DW     LSTATE
3036 BASE:           .DW     SUSE
3037                 .DB pBASE
3038 
3039 ;#DPL
3040 ; DPL            ( −− a )                             Переменная, позиция последней запятой в последнем введённом числе от конца.
3041 ; Форт−79 определяет DPL правильно. Но при этом почему−то говорит о выводе, а не о вводе числа.
3042 LDPL:           .DB     3
3043                 .TEXT   "DPL"
3044                 .DW     LBASE
3045 DPL:            .DW     SUSE
3046                 .DB pDPL
3047 
3048 ;#CSP
3049 ; CSP            ( −− a )                             Переменная, контрольное значение указателя стека данных.
3050 LCSP:           .DB     3
3051                 .TEXT   "CSP"
3052                 .DW     LDPL
3053 CSP:            .DW     SUSE
3054                 .DB pCSP
3055 
3056 ;#Rnum
3057 ; R#             ( −− a )                             Переменная, позиция курсора при редактировании экрана или возникновении ошибки.
3058 ; Позиция курсора при редактировании экрана.
3059 ; Также сюда записывается указатель >IN когда был выведен последний MESSAGE
3060 LRNUM:          .DB     2
3061                 .TEXT   "R#"
3062                 .DW     LCSP
3063 RNUM:           .DW     SUSE
3064                 .DB pRNUM
3065 
3066 ;#HLD
3067 ; HLD            ( −− a )                             Переменная, указатель вершины буфера PAD.
3068 ; Позиция последней литеры, перенесённой в буфер PAD словом HOLD
3069 LHLD:           .DB     3
3070                 .TEXT   "HLD"
3071                 .DW     LRNUM
3072 HLD:            .DW     SUSE
3073                 .DB pHLD
3074 
3075 ;#EE
3076 ; EE             ( −− a )                             Переменная, порядок вводимого числа.
3077 LEXP:           .DB     2
3078                 .TEXT   "EE"                            ; $EX в Форт ИТЭФ
3079                 .DW     LHLD
3080 SEXP:           .DW     SUSE
3081                 .DB pEXP
3082 
3083 ;#ERB
3084 ; ERB            ( −− a )                             Переменная, флаг блокировки выхода в QUIT при ошибке ERROR.
3085 ; Если ERB=0, ERROR уходит в систему Форт
3086 ; В противном случае переменная ERB обнуляется, а уход из программы в Форт через QUIT блокируется.
3087 ; Блокировка осуществляется как при "неузнанном" имени, так и при неправильном вводе чисел,
3088 ; включая ошибки, связанные с конфликтами по системе счисления.
3089 ; Ошибку считывания состояния системы по LOAD" заблокировать невозможно.
3090 LERB:           .DB     3
3091                 .TEXT   "ERB"
3092                 .DW     LEXP
3093 ERB:            .DW     SUSE
3094                 .DB pERB
3095 
3096 ;#SAVIN
3097 ; SAVIN          ( −− a )                             Переменная, значение >IN для NUMBER и BASE для FL.
3098 ; Сохраняем >IN в INTERPRET для NUMBER и BASE для FL
3099 LSAVIN:         .DB     5
3100                 .TEXT   "SAVIN"
3101                 .DW     LERB
3102 SAVIN:          .DW     SUSE
3103                 .DB pSAVIN
3104 
3105 ;               ∗∗ Слова высокого уровня ∗∗
3106 
3107 ;#Comma
3108 ; ,              ( d −− )                             Скомпилировать d в первую свободную ячейку словаря.
3109 ; Компилирует число n в первую свободную ячейку словаря.
3110 LCOMMA:         .DB     1
3111                 .TEXT   ","                             ; ( n −− ) F79
3112                 .DW     LSAVIN
3113 COMMA:          .DW CALL
3114 RCOMMA:         .DW HERE,STORE, TWO,ALLOT, EXIT
3115 
3116 ;#CComma
3117 ; C,             ( b −− )                             Скомпилировать b в первый свободный байт словаря.
3118 ; Компилирует байт c в очередной свободный байт словаря.
3119 LCCOM:          .DB     2
3120                 .TEXT   "C,"                            ; ( c −− ) F79
3121                 .DW     LCOMMA
3122 CCOM:           .DW CALL, HERE,CSTOR, ONE,ALLOT, EXIT
3123 
3124 ;#deComma
3125 ; Д,             ( x −− )                             Скомпилировать x в первый свободный регистр десятичного словаря.
3126 ; Компилирует число x в первую свободную ячейку десятичного словаря.
3127 LDCOMMA:        .DB     2,132
3128                 .TEXT   ","                             ; "Д," ( x −− ) К
3129                 .DW     LCCOM
3130 DCOMMA:         .DW CALL, DHERE,STORE, ONE,DALLOT, EXIT
3131 
3132 ;#LAST
3133 ; LAST           ( −− a )                             Дать NFA последней созданной статьи.
3134 ; Даёт ссылку на слово, определённое последним в текущем наборе слов.
3135 LLATES:         .DB     4
3136                 .TEXT   "LAST"                          ; ( −− NFA )
3137                 .DW     LDCOMMA
3138 LATES:          .DW CALL, CURR,UAT,UAT, EXIT
3139 
3140 ;#SPACE
3141 ; SPACE          ( −− )                               Пробел. Вывести пробел на индикатор.
3142 ; Вывод одного пробела.
3143 ; Каллисто выдаёт обычный пробел, который по ширине может быть тоньше цифры.
3144 LSPACE:         .DB     5
3145                 .TEXT   "SPACE"                         ; ( −−) F79
3146                 .DW     LLATES
3147 SPACE:          .DW CALL
3148 RSPACE:         .DW BL,EMIT, EXIT
3149 
3150 ;#NtoLINK
3151 ; N>LINK         ( a1 −− a2 )                         От имени к связи. Преобразовать NFA в LFA.
3152 ; Слово из расширения стандарта Форт−83 (необязательное).
3153 LNLINK:         .DB     6
3154                 .TEXT   "N>LINK"                        ; ( nfa −− lfa ) F83
3155                 .DW     LSPACE
3156 NLINK:          .DW CALL, DUP,CAT,BL,MOD,PLUS,ONEP,EXIT ; BL == 32
3157 
3158 ;#NAMEfrom
3159 ; NAME>          ( a −− т )                           Получить токен по имени. Преобразовать NFA в CFA.
3160 ; Слово из расширения стандарта Форт−83 (необязательное).
3161 LN2CFA:         .DB     5
3162                 .TEXT   "NAME>"                         ; ( nfa −− cfa ) F83
3163                 .DW     LNLINK
3164 N2CFA:          .DW CALL
3165 RN2CFA:         .DW DUP,CAT,BL,MOD, PLUS, LITB          ; BL == 32
3166                 .DB 3
3167                 .DW PLUS, EXIT
3168 
3169 ;#NtoBODY
3170 ; N>BODY         ( a1 −− a2 )                         От имени к телу. Преобразовать NFA в PFA.
3171 ; Тоже самое, что последовательность NAME> >BODY
3172 LN2PFA:         .DB     6
3173                 .TEXT   "N>BODY"                        ; ( nfa −− pfa ) К
3174                 .DW     LN2CFA
3175 N2PFA:          .DW CALL, DUP,CAT,BL,MOD, PLUS, LITB    ; BL == 32
3176                 .DB 5
3177                 .DW PLUS, EXIT
3178 
3179 ;#qERROR
3180 ; ?ERROR         ( ф c −− )                           Сгенерировать ошибку номер c, если флаг ф истинен (ф<>0).
3181 LQERR:          .DB     6
3182                 .TEXT   "?ERROR"                        ; ( флаг номер −− ) ?ERROR диагностика ошибок Форт ИТЭФ
3183                 .DW     LN2PFA
3184 QERR:           .DW CALL
3185 RQERR:          .DW SWAP, ZBRAN,TTT, BRAN,RERROR        ; +++ Можно сократить с помощью ZEQU ?
3186 TTT:            .DW DROP,EXIT
3187 
3188 ;#qCOMP
3189 ; ?COMP          ( −− )                               Сгенерировать ошибку 17, если нет состояния компиляции.
3190 LQCOMP:         .DB     5
3191                 .TEXT   "?COMP"
3192                 .DW     LQERR
3193 QCOMP:          .DW CALL, STATE,UAT,ZEQU, LITB
3194                 .DB 17
3195                 .DW BRAN,RQERR
3196 
3197 ;#qEXEC
3198 ; ?EXEC          ( −− )                               Сгенерировать ошибку 18, если нет состояния исполнения.
3199 LQEXEC:         .DB     5
3200                 .TEXT   "?EXEC"
3201                 .DW     LQCOMP
3202 QEXEC:          .DW CALL, STATE,UAT, LITB
3203                 .DB 18
3204                 .DW BRAN,RQERR
3205 
3206 ;#qPAIRS
3207 ; ?PAIRS         ( y x −− )                           Сгенерировать ошибку 19, если x отлично от y.
3208 LQPAIR:         .DB     6
3209                 .TEXT   "?PAIRS"
3210                 .DW     LQEXEC
3211 QPAIR:          .DW CALL, SUB, LITB
3212                 .DB 19
3213                 .DW BRAN,RQERR
3214 
3215 ;#qCSP
3216 ; ?CSP           ( −− )                               Сгенерировать ошибку 20, если указатель стека отличен от CSP.
3217 ; Выдать ошибку "сбился указатель стека" если он не равен значению в "CSP".
3218 LQCSP:          .DB     4
3219                 .TEXT   "?CSP"
3220                 .DW     LQPAIR
3221 QCSP:           .DW CALL, SPAT,CSP,UAT,SUB, LITB
3222                 .DB 20
3223                 .DW BRAN,RQERR
3224 
3225 ;#qLOADING
3226 ; ?LOADING       ( −− )                               Сгенерировать ошибку 22, если входной текст идёт не с экрана.
3227 LQLOAD:         .DB     8
3228                 .TEXT   "?LOADING"
3229                 .DW     LQCSP
3230 QLOAD:          .DW CALL, BLK,UAT,ZEQU, LITB            ; BLK U@ 0= 22 ?ERROR
3231                 .DB 22
3232                 .DW BRAN,RQERR
3233 
3234 ;#COMPILE
3235 ; COMPILE        ( −− )                               Скомпилировать токен −− пару байт, следующих за COMPILE.
3236 ; Компиляция 16−битного слова, следующего за оператором.
3237 ; Обычно это токен (CFA).
3238 LCOMP:          .DB     7
3239                 .TEXT   "COMPILE"                       ; F79
3240                 .DW     LQLOAD                          ; ?COMP R> DUP 2+ >R U@ ,
3241 COMP:           .DW CALL, QCOMP, FROMR, DUP,TWOP,TOR, UAT, BRAN,RCOMMA
3242 
3243 ;#SMUDGE
3244 ; SMUDGE         ( −− )                               Изменить признак видимости последней созданной статьи.
3245 ; Изменить флаг "SMUDGE" в последней созданной статье.
3246 LSMUG:          .DB     6
3247                 .TEXT   "SMUDGE"
3248                 .DW     LCOMP
3249 SMUG:           .DW CALL                                ; LAST BL TOGGLE
3250 RSMUG:          .DW LATES,BL,TOGL,EXIT                  ; BL == 32 в некоторых системах константа &HIDDEN
3251 
3252 ;#xSemiCODE
3253 ; (;CODE)        ( −− )                               Записать в поле кода LAST следующий адрес и EXIT. (Слово без заголовка.)
3254 ; Слово (;CODE) невозможно использовать в прикладных программах, т.к. код ЯМК выполняется лишь из памяти программ.
3255 ; Для экономии места слово (;CODE) лишено заголовка.
3256 ;
3257 ; Записать в поле кода последней статьи следующий адрес и закончить определение.
3258 ;LPSCOD:        .DB     7
3259 ;               .TEXT   "("                             ; "(;CODE)"
3260 ;               .DB     3BH
3261 ;               .TEXT   "CODE)"
3262 ;               .DW     LSMUG                           ; >R LAST NAME> !
3263 PSCOD:          .DW CALL, FROMR, LATES,N2CFA, STORE, EXIT
3264 
3265 ;#BUILDS
3266 ; <BUILDS        ( −− )                               Начало определяющей части порождающего слова.
3267 ; <BUILDS переводится с Форта на русский как "компилируется".
3268 ; Слово взято из Форта−79 и ранее, в Форте−83 и последующих вместо него можно использовать просто CREATE
3269 LBUILD:         .DB     7
3270                 .TEXT   "<BUILDS"                       ; ( −− a) F79
3271                 .DW     LSMUG
3272 BUILD:          .DW CALL, ZERO,BRAN,RCON                ; 0 CONSTANT
3273 
3274 ;#COUNT
3275 ; COUNT          ( a −− a1 U )                        Подсчёт. Даёт адрес a1 и длину U строки со счётчиком a.
3276 ; Преобразование строки со счётчиком в адрес−длина.
3277 LCOUNT:         .DB     5
3278                 .TEXT   "COUNT"                         ; ( a −− a+1 u ) F79
3279                 .DW     LBUILD
3280 COUNT:          .DW CALL, DUP,ONEP,SWAP,CAT, EXIT       ; DUP 1+ SWAP C@
3281 
3282 ;#XDotq
3283 ; (.")           ( −− )                               Слово, компилируемое в ."
3284 LPDOTQ:         .DB     4                               ; (.")
3285                 .TEXT   "(."
3286                 .DB     34
3287                 .TEXT   ")"
3288                 .DW     LCOUNT
3289 PDOTQ:          .DW CALL, I,COUNT,DUP,ONEP              ; I COUNT DUP 1+
3290                 .DW FROMR,PLUS,TOR,TYPE,EXIT            ; R> + >R TYPE
3291 
3292 ;#Dotq
3293 ; ."             ( −− )                               При исполнении вывести строку, ограниченную кавычкой.
3294 ; ." XXX" выводит строку XXX на индикатор во время выполнения определяемого слова.  Код " завершает строку.
3295 ; Это слово нельзя употреблять вне определения через двоеточие.
3296 LDOTQ:          .DB     82H
3297                 .TEXT   "."                             ; ." F83
3298                 .DB     34
3299                 .DW     LPDOTQ
3300 DOTQ:           .DW CALL, COMP,PDOTQ,LITB
3301                 .DB 34                                  ; '"'
3302                 .DW WORD,CAT,ONEP,ALLOT,EXIT
3303 
3304 ;#Dotp
3305 ; .(             ( −− )                               При компиляции вывести строку, вплоть до закрывающей скобки.
3306 ; Выводит текст сообщения, ограниченного правой круглой скобкой.
3307 ; Может использоваться как вне, так и внутри определений через двоеточие.
3308 LCOMQ:          .DB     82H
3309                 .TEXT   ".("                            ; F83
3310                 .DW     LDOTQ
3311 COMQ:           .DW CALL, LITB
3312                 .DB 41                                  ; '('
3313                 .DW WORD,COUNT,TYPE,EXIT
3314 
3315 ;#QUERY
3316 ; QUERY          ( −− )                               Ввести строку с пульта в TIB, ограничив её двумя нулями.
3317 ; Осуществляет ввод строки символов с клавиатуры.
3318 ; Ввод прекращается, если нажата клавиша Ввод или заполнен входной буфер.
3319 ; Форт−79 устанавливает максимальный размер входной строки 80 символов.
3320 ; В Каллисто максимальный размер строки 94 символа.
3321 LQUERY:         .DB     5
3322                 .TEXT   "QUERY"                         ; F79
3323                 .DW     LCOMQ
3324 QUERY:          .DW CALL, ZERO,TIB,DUP,LITB
3325                 .DB 94
3326                 .DW ACCE,PLUS,STORE, IN,ZSTORE, BRAN,RSPACE
3327 
3328 ;#null
3329 ; без имени      ( −− )                               Пустышка. Закончить интерпретацию входного потока.
3330 ; Нулевое слово прерывает цикл INTERPRET
3331 ; Старый, но весьма сомнительный приём.  После перехода к Форту−2012 будет удалён.
3332 LNULL:          .DB     81H
3333                 .DB     0                               ; "\0"
3334                 .DW     LQUERY
3335 NULL:           .DW CALL, BLK,UAT,ZBRAN,NUL
3336                 .DW ONE,BLK,PSTOR, IN,ZSTORE, QEXEC     ; Автоматом на следующий экран для THRU
3337 NUL:            .DW LEV,EXIT
3338 
3339 ;#PAD
3340 ; PAD            ( −− a )                             Адрес буфера промежуточного хранения последовательности литер.
3341 ; Внимание, это не переменная!
3342 ; Выдаёт адрес буфера для промежуточного хранения последовательностей символов
3343 ; Положение временного буфера жёстко связано с верхней границей словаря, определяемой оператором HERE
3344 ; PAD в Форте−79 требует как минимум 64 байта.
3345 ; PAD в Форте−83 требует как минимум 84 байта.
3346 LPAD:           .DB     3
3347                 .TEXT   "PAD"                           ; F79
3348                 .DW     LNULL
3349 PAD:            .DW CALL, HERE,LITB
3350                 .DB 84
3351                 .DW PLUS,EXIT
3352 
3353 ;#WORD
3354 ; WORD           ( c −− a )                           Ввести слово до стоп−литеры c. Дать его адрес как строки со счётчиком.
3355 ; Считывает одно слово из входного или экранного буфера и размещает его, начиная с адреса HERE
3356 ; Первый байт содержит число символов в слове.
3357 ; Кладёт адрес считанного слова в стек, как это требует Форт−79.
3358 ; Этот адрес a всегда HERE −− как и в большинстве реализаций Форта.
3359 LWORD:          .DB     4
3360                 .TEXT   "WORD"                          ; ( c "<c>ccc<c>" −− HERE ) F79 F83
3361                 .DW     LPAD
3362 WORD:           .DW CALL, BLK,UAT, QDUP,ZBRAN,WD1, BLOCK, BRAN,WD2
3363 WD1:            .DW TIB
3364 WD2:            .DW IN,UAT, PLUS, SWAP, ENCL
3365                 .DW HERE, BL,BLANK                      ; BL == 32  ( 31 имя и 1 байт длины)
3366                 .DW IN,PSTOR, DUP,TOR, HERE,CSTOR
3367                 .DW HERE,ONEP, FROMR, CMOVE, HERE, EXIT
3368 
3369 ;#CONVERT
3370 ; CONVERT        ( u a −− u1 a1 )                     Преобразовать u и литеры от a+1 в u1 и a1 −− адрес первой не цифры.
3371 LPNUMB:         .DB     7
3372                 .TEXT   "CONVERT"                               ; F79 F83
3373                 .DW     LWORD
3374 PNUMB:          .DW CALL
3375 BN:             .DW ONEP, DUP,TOR, CAT, BASE,UAT, DIGIT, ZBRAN,MMO
3376                 .DW SWAP, BASE,UAT,STAR, PLUS
3377                 .DW DPL,AT, ONEP, ZBRAN,BN1, ONE,DPL,PSTOR      ; Увеличить DPL на единицу, если там не −1
3378 BN1:            .DW FROMR, BRAN,BN
3379 MMO:            .DW FROMR, EXIT
3380 
3381 ;#FINDN
3382 ; FINDN          ( a −− a1 c 1 | 0 )                  Поиск слова в активных наборах. При успехе дать NFA и байт счётчика.
3383 ; Поиск слова в активных наборах слов
3384 ; a −− адрес начала счётной строки, содержащей имя слова, которое нужно найти в словаре
3385 ; В отличии от Форта ИТЭФ a1 это NFA
3386 ; −FIND Форта ИТЭФ может быть имитирован BL WORD FINDN NAME>
3387 ; Может изменить строчку a, переведя всё в заглавные буквы.
3388 ; +++ Доработать возвращаемые параметры до Форта−83
3389 LFIND:          .DB     5
3390                 .TEXT   "FINDN"                         ; ( a −− nfa c 1 | 0 ) К
3391                 .DW     LPNUMB
3392 FIND:           .DW CALL, DUP,TOR, COUNT, UPPER
3393                 .DW CONT,UAT, CURR,UAT, SUB, ZBRAN,ONCE ; Если словарь один, ускоряем поиск вдвое
3394                 .DW I, CONT,UAT,UAT, PFIND, QDUP, ZEQU, ZBRAN,FIN
3395 ONCE:           .DW I, LATES, PFIND
3396 FIN:            .DW LEV, EXIT                           ; R> DROP
3397 
3398 ;#NUMBER
3399 ; NUMBER         ( a −− n )                           Преобразовать строку в целое число n, начиная с a+1.
3400 ; Преобразует в число последовательность символов, начиная с a+1 с учётом BASE,
3401 ; a может содержать число символов в строке.
3402 ; Подразумевается, что после числа всегда идёт пробел, а строчные буквы уже преобразованы в заглавные.
3403 ; Переписать для Форта−2012 и требований Каллисто
3404 ; Трюк с SAVIN делает NUMBER при обработке 'c' интимно зависимым от INTERPRET
3405 ; при переписывании под стандарт символьное число придётся вынести в отдельное слово
3406 ; Слово присутствует ещё в Форте−79, но у нас оно возвращает одну ячейку стека, а не двойную.
3407 ; +++ Возможно, стоит сделать вызов FL здесь.
3408 ; +++ Убрать зависимость от WORD и входного потока
3409 LNUMB:          .DB     6
3410                 .TEXT   "NUMBER"                                ; ( адрес −− число ) К
3411                 .DW     LFIND
3412 NUMB:           .DW CALL, SEXP,ZSTORE                           ; Обнуляем порядок
3413                 .DW TRUE,DPL,STORE                              ; DPL := −1
3414                 .DW BASE,UAT, ZERO, ROT                         ; 0 значит положительное число
3415                 .DW ONEP,DUP, CAT
3416                 .DW DUP,LITB
3417                 .DB 39                                          ; "'"
3418                 .DW EQUAL, ZBRAN,NH2
3419                 .DW DROP, SAVIN,UAT, IN,STORE, BL,WORD, DROP    ; Восстанавливаем регистр символа
3420                 .DW ONEP,DUP, CAT, SWAP                         ; Символ
3421                 .DW ONEP,DUP, CAT, LITB
3422                 .DB 39                                          ; "'"
3423                 .DW SUB, ZBRAN,NH21
3424                 .DW DROP, BRAN,ER2
3425 NH21:           .DW ONEP,CAT, BL,EQUAL, ZBRAN,ER2
3426 NH22:           .DW ROT,ROT, DDROP, EXIT                        ; Выход, символ определён
3427 NH2:            .DW DUP, LITB
3428                 .DB 35                                          ; "#"
3429                 .DW EQUAL, ZBRAN,NH01
3430                 .DW DROP, DEC, BRAN,NH03                        ; Десятичное число
3431 NH01:           .DW DUP, LITB
3432                 .DB 36                                          ; "$"
3433                 .DW EQUAL, ZBRAN,NH02
3434                 .DW DROP, HEX, BRAN,NH03                        ; Шестнадцатеричное число
3435 NH02:           .DW LITB
3436                 .DB 37                                          ; "%"
3437                 .DW EQUAL, ZBRAN,NH04
3438                 .DW TWO,BASE,STORE                              ; Двоичное число
3439 NH03:           .DW ONEP
3440 NH04:           .DW DUP,CAT, LITB
3441                 .DB 45                                          ; "−"
3442                 .DW EQUAL, ZBRAN,NH3
3443                 .DW SWAP,ONEP, SWAP,ONEP                        ; 1 значит отрицательное число
3444 NH3:            .DW ONEM
3445 NH4:            .DW ZERO, SWAP, PNUMB
3446                 .DW DUP,CAT, BL,SUB, ZBRAN,NH6
3447                 .DW DUP,CAT, LITB
3448                 .DB 44                                          ; ","
3449                 .DW EQUAL, ZBRAN,NEXP
3450                 .DW DPL,ZSTORE, PNUMB, DUP,CAT, BL,SUB, ZBRAN,NH6
3451 NEXP:           .DW DPL,AT, ZERO,MAX, SWAP                      ; Теперь числа с E, но без запятой ловятся по DPL
3452                 .DW DUP,CAT, LITB
3453                 .DB 69                                          ; "E"
3454                 .DW EQUAL, ZBRAN,ER1
3455                 .DW ONEP, DUP,CAT, LITB
3456                 .DB 45                                          ; "−"
3457                 .DW EQUAL, ZBRAN,NEMI, ONE, BRAN,NH0
3458 NEMI:           .DW ONEM, ZERO
3459 NH0:            .DW SWAP,ZERO,SWAP, PNUMB, CAT, BL,EQUAL, ZBRAN,ER
3460                 .DW SWAP, ZBRAN,NH5, NEGATE                     ; Если был "−", изменить знак порядка
3461 NH5:            .DW SEXP,STORE, DPL,STORE, ZERO
3462 NH6:            .DW DROP, SWAP, ZBRAN,NH7, NEGATE               ; Если был "−", изменить знак числа
3463 NH7:            .DW SWAP, BASE, DUP,UAT,SAVIN,STORE, STORE, EXIT
3464 ER:             .DW DROP
3465 ER1:            .DW DDROP
3466 ER2:            .DW DDROP
3467                 .DW BASE, DUP,UAT,SAVIN,STORE, STORE,ZERO,ERROR ; Эксперимент: восстановим систему счисления при ошибке
3468                 .DW ZERO, EXIT                                  ; Возвращаем 0, если выключена обработка ошибок
3469 
3470 ;#FL
3471 ; FL             ( n −− x )                           Преобразовать целое, возвращённое NUMBER в десятичное число.
3472 ; Слово из расширения Форта ИТЭФ вошло в ядро Каллисто, возможно нуждается в переименовании.
3473 LFL:            .DB     2
3474                 .TEXT   "FL"                                    ; ( n −− f ) В Каллисто вызывается автоматически
3475                 .DW     LNUMB
3476 FL:             .DW CALL, SAVIN,UAT                             ; В SAVIN слово NUMBER записало использовавшуюся систему счисления
3477                 .DW SEXP,AT, DPL,AT, SUB
3478                 .DW DUP, LITP,333,LESS, ZBRAN,FLER1             ; Робкая попытка защититься от переполнения без учёта BASE (2^333).
3479                 .DW POWER, STAR, EXIT
3480 FLER1:          .DW DDROP, ZERO, BRAN,RERROR
3481 
3482 ;#ERROR
3483 ; ERROR          ( c −− )                             Вывести сообщение об ошибке c и уйти в QUIT если ERB=0.
3484 ; Уход в среду Каллисто, если ERB=0
3485 LERROR:         .DB     5
3486                 .TEXT   "ERROR"                                 ; ( номер −− ) Диагностика ошибок Форт ИТЭФ
3487                 .DW     LNUMB
3488 ERROR:          .DW CALL
3489 RERROR:         .DW HERE,COUNT,TYPE, PDOTQ
3490                 .DB 3
3491                 .TEXT " ? "
3492                 .DW ERB,UAT, ZBRAN,XER
3493                 .DW ERB,ZSTORE, DROP, EXIT
3494 XER:            .DW MESS, SPSTO, DISKOFF, BRAN,RQUIT            ; Каллисто также запрещает дисковые операции
3495 
3496 ;#IDd
3497 ; ID.            ( a −− )                             Напечатать имя слова (по NFA) и дать пробел.
3498 ; Печатает имя слова, записанное в соответствии с требованиями словаря Форта
3499 LIDDOT:         .DB     3
3500                 .TEXT   "ID."
3501                 .DW     LERROR
3502 IDDOT:          .DW CALL, COUNT, BL,MOD, TYPE, BRAN,RSPACE      ; BL == 32
3503 
3504 ;#CREATE
3505 ; CREATE         ( −− )                               Создать начало статьи (до PFA) для следующего слова.
3506 ; Формирует слово с именем XXX (заголовок и CFA), оставляя его поле параметров пустым.
3507 ; При обращении к XXX в стек записывается его адрес.
3508 ; Длина имени слова ограничивается 31 литерой
3509 ; Слово присутствует в Форте−79 и последующих, но имеет небольшие отличия из−за принятия на себя функций <BUILDS
3510 LCREAT:         .DB     6
3511                 .TEXT   "CREATE"                ; ( "<пр>имя" −− ) К
3512                 .DW     LIDDOT
3513 CREAT:          .DW CALL, BL,WORD, FIND, ZBRAN,CRE
3514                 .DW DROP,IDDOT,LITB             ; Наш FIND возвращает NFA
3515                 .DB 4
3516                 .DW MESS                        ; Сообщение, что такое слово уже есть
3517 CRE:            .DW HERE, DUP,CAT, LITB
3518                 .DB 31
3519                 .DW MIN                         ; Обрезать длину по 31 литеру
3520                 .DW DUP, CCOM                   ; биты SMUDGE и IMMEDIATE сброшены
3521                 .DW ALLOT                       ; Оставить слово как поле имени
3522                 .DW LATES, COMMA                ; NFA прошлого слова в поле связи
3523                 .DW CURR,UAT, STORE             ; Сделать слово последним в словаре CURRENT @
3524                 .DW LITP,SCRE, BRAN,RCOMMA      ; CFA: созданное слово кладёт на стек свой PFA
3525 SCRE:           RM7 10002 + KM3 KGOTO9          ; Созданные слова размещаются в области двоичных данных
3526 
3527 ;#BracketCOMPILE
3528 ; [COMPILE]      ( −− )                               Скомпилировать следующее слово, независимо от его признака IMMEDIATE.
3529 ; Заставляет следующее слово скомпилироваться, даже если оно обладает признаком немедленного исполнения.
3530 ; Используется внутри определений через двоеточие.
3531 ; Сейчас используется мало, в моде POSTPONE
3532 LBCOM:          .DB     89H
3533                 .TEXT   "[COMPILE]"             ; ( "<пр>имя" −−) F79
3534                 .DW     LCREAT
3535 BCOM:           .DW     CALL, BL,WORD, FIND, ZEQU, ZERO,QERR, DROP, N2CFA, BRAN,RCOMMA
3536 
3537 ;#TO
3538 ; TO             ( x −− )                             Запись в десятичную величину или переменную действия.
3539 LTO:            .DB     82H
3540                 .TEXT   "TO"                    ; ( x "<пр>имя" −− ) F12
3541                 .DW     LBCOM
3542 TO:             .DW CALL, BL,WORD, FIND, ZEQU, ZERO,QERR, DROP, N2PFA, TWOP
3543                 .DW STATE,UAT, ZBRAN,TO1, COMMA, EXIT
3544 TO1:            .DW EXEC, EXIT
3545 
3546 ;#LITERAL
3547 ; LITERAL        ( D −− | D )                         Скомпилировать D в литерал. В режиме исполнения оставляет D на стеке.
3548 ; 1. При компиляции переносит код n из стека в описание нового слова,
3549 ; а при интерпретации описанного слова записывает в стек указанное число n
3550 ; Скомпилированный код может зависеть от значения компилируемого числа.
3551 ; 2. При исполнении оставляет n на вершине стека.
3552 LLITER:         .DB     87H
3553                 .TEXT   "LITERAL"               ; ( n −− ) F79
3554                 .DW     LTO
3555 LITER:          .DW CALL
3556 RLITER:         .DW STATE,UAT, ZBRAN,REXIT, COMP,LITD, BRAN,RCOMMA
3557 
3558 ;#FLITERAL
3559 ; FLITERAL       ( x −− | x )                         Скомпилировать x в литерал. В режиме исполнения оставляет x на стеке.
3560 ; Тоже, что и LITERAL, но для плавучки.
3561 ; Литерал компилируется в десятичный словарь, откуда его читает (ИП)
3562 LFLITE:         .DB     88H
3563                 .TEXT   "FLITERAL"
3564                 .DW     LLITER
3565 FLITE:          .DW CALL, STATE,UAT, ZBRAN,REXIT, COMP,XREG, DCOMMA, DHERE, LITP
3566                 .DB 39,17                       ; 10001
3567                 .DW SUB, BRAN,RCOMMA
3568 
3569 ;#qSTACK
3570 ; ?STACK         ( −− )                               Сгенерировать ошибку 1, если стек вычерпан (или переполнен).
3571 LQSTAC:         .DB     6
3572                 .TEXT   "?STACK"
3573                 .DW     LFLITE
3574 QSTAC:          .DW     CALL
3575                 .DW SZERO,UAT, ONEM, SPAT, LESS, ONE, BRAN,RQERR
3576 ;               .DW LITP
3577 ;               .DB 255,80H                     ; .DW −80H не работает, т.к. литералы все положительные в памяти программ
3578 ;               .DW SPAT,LESS,TWO,BRAN,RQERR    ; +++ проверить с ДHERE на сохранность десятичных переменных
3579 
3580 ;#INTERPRET
3581 ; INTERPRET      ( −− )                               Интерпретировать входной поток.
3582 ; Интерпретирует последовательность слов, пока там что−то есть.
3583 LINTER:         .DB     9
3584                 .TEXT   "INTERPRET"             ; F79
3585                 .DW     LQSTAC
3586 INTER:          .DW CALL
3587 IT1:            .DW IN,UAT, SAVIN,STORE         ; Сохраняем информацию, важную для NUMBER
3588                 .DW BL,WORD, FIND, ZBRAN,IT3, STATE,UAT, LESS
3589                 .DW ZBRAN,IT2, N2CFA, COMMA, BRAN,IT5
3590 IT2:            .DW N2CFA, EXEC, BRAN,IT5
3591 IT3:            .DW HERE, NUMB, DPL,AT, ONEP, ZBRAN,IT4, FL, FLITE, BRAN,IT5
3592 IT4:            .DW LITER                       ; Проверить здесь стек
3593 IT5:            .DW QSTAC, BRAN,IT1
3594 
3595 ;#IMMEDIATE
3596 ; IMMEDIATE      ( −− )                               Изменить признак IMMEDIATE последней созданной статьи.
3597 ; Преобразует слово, за описанием которого следует, в оператор, исполняемый при компиляции.
3598 ; В некоторых Фортах не переключает, а строго устанавливает. Как его там сбросить? :−)
3599 LIMMED:         .DB     9
3600                 .TEXT   "IMMEDIATE"             ; F79
3601                 .DW     LINTER
3602 IMMED:          .DW CALL, LATES, LITB
3603                 .DB 128                         ; В некоторых системах вместо 128 константа &IMMEDIATE
3604                 .DW TOGL, EXIT
3605 
3606 ;#VOCABULARY
3607 ; VOCABULARY     ( −− )                               Определить следующее слово, как набор слов над набором CURRENT.
3608 ; Порождающее слово, которое создаёт новые наборы слов.
3609 ; Исполнение созданного слова делает набор контекстом CONTEXT
3610 LVOCAB:         .DB     10
3611                 .TEXT   "VOCABULARY"            ; F79
3612                 .DW     LIMMED
3613 VOCAB:          .DW CALL, BUILD, LITP
3614                 .DB 1,32                        ; Здесь может потребоваться переставить байты местами
3615                 .DW COMMA, CURR,UAT, CFA, COMMA
3616                 .DW HERE, VOCLINK,UAT, COMMA, VOCLINK,STORE, DOESP
3617 DOVOC:          .DW TWOP, CONT,STORE, EXIT
3618 
3619 ;#p
3620 ; (              ( −− )                               Комментарий. Игнорировать входные литеры до закрывающей круглой скобки.
3621 ; Комментарий. Игнорировать всё до первой встречной ")" или конца строки (терминал) / экрана (интерпретация блока).
3622 LPAREN:         .DB     81H
3623                 .TEXT   "("                     ; F79
3624                 .DW     LVOCAB
3625 PAREN:          .DW CALL, LITB
3626                 .DB 41                          ; ")"
3627                 .DW WORD, DROP, EXIT
3628 
3629 ;#QUIT
3630 ; QUIT           ( −− )                               Передать управление на пульт. Ждёт команд молча, без приглашения.
3631 ; Очищает оба стека и возвращает управление на пульт Каллисто.
3632 ; Не выдаётся никаких сообщений.
3633 ; Основной оператор Форта, резидентный монитор системы Форт.
3634 ;
3635 ; Очищает стек возвратов, устанавливает состояние исполнения и возвращает управление терминалу. (F79)
3636 LQUIT:          .DB     4
3637                 .TEXT   "QUIT"                  ; F79
3638                 .DW     LPAREN
3639 QUIT:           .DW     CALL
3640 RQUIT:          .DW BLK,ZSTORE, LBRAC
3641 QUI:            .DW RPSTO, CR, QUERY, INTER, STATE,UAT
3642                 .DW ZEQU, ZBRAN,QUI, PDOTQ
3643                 .DB 3
3644                 .TEXT " ok"                     ; Это приглашение не будет видно при 2 FONT!
3645                 .DW BRAN,QUI
3646 
3647 ;#ABORT
3648 ; ABORT          ( −− )                               Очищает стек, устанавливает набор слов FORTH и молча делает QUIT.
3649 ; Прерывает исполнение, делает набор FORTH контекстным, исполняет QUIT
3650 ; Распечатывает имя интерпретатора. (у нас нет такого!)
3651 ;
3652 ; Очистить стеки возвратов и данных, установить состояние исполнения. Вернуть контроль терминалу. (F79)
3653 ;
3654 ; Очистка стека данных и выполнение функций слова QUIT. Сообщения не выдаются (Броуди)
3655 ;
3656 ; Используется при инициализации системы, обработке всевозможных ошибок
3657 ; и выходе системы из нештатных ситуаций.
3658 LABORT:         .DB     5
3659                 .TEXT   "ABORT"                 ; F79 В Форте−2012 есть ABORT и ABORT"
3660                 .DW     LQUIT
3661 ABORT:          .DW CALL
3662 RABORT:         .DW SPSTO, RAD, DEC
3663                 .DB 2bH,8fH                     ; 11151 FORTH (xt)
3664                 .DW DEFIN, BRAN,RQUIT
3665 
3666 ;#EMPTY−BUFFERS
3667 ; EMPTY−BUFFERS  ( −− )                               Очистить буфер, независимо от содержания. Ничего на диск не записывать.
3668 ; Буфер отмечается как пустой, независимо от содержания. Обновлённый блок на диск не записывается.
3669 ; По хорошему содержимое буфера можно не стирать, даже быстрее будет.
3670 LMTBUF:         .DB     13
3671                 .TEXT   "EMPTY−BUFFERS"         ; F79 F84
3672                 .DW     LABORT
3673 MTBUF:          .DW CALL
3674 RMTBUF:         .DW BUFN, LITP,3074, ERASE, EXIT
3675 
3676 ;#SAVE−BUFFERS
3677 ; SAVE−BUFFERS   ( −− )                               Если установлен флаг UPDATE, сбросить блок на диск.
3678 ; Если находящийся в буфере экран был изменён, скидывает его на диск.
3679 ; Буфер остаётся распределённым.
3680 LSVBUF:         .DB     12
3681                 .TEXT   "SAVE−BUFFERS"          ; F79 F84
3682                 .DW     LMTBUF
3683 SVBUF:          .DW CALL, BUFN,AT, ZLESS, ZBRAN,REXIT
3684                 .DW ZERO,BUFN,CSTOR             ; Сбросить флаг UPDATE
3685                 .DW BUFN,UAT, ZERO, BRAN,RRW    ; Записать буфер на диск
3686 
3687 ;#FLUSH
3688 ; FLUSH          ( −− )                               Если буфер изменён, сохранить на диск. Очистить буфер.
3689 ; Если экран изменён, скидывает его на диск и выкидывает его из памяти.
3690 LFLUSH:         .DB     5
3691                 .TEXT   "FLUSH"                 ; F83
3692                 .DW     LSVBUF
3693 FLUSH:          .DW CALL
3694 RFLUSH:         .DW SVBUF                       ; Записать буфер на диск
3695                 .DW BRAN,RMTBUF                 ; Очистить буфер
3696 
3697 ;#BUFFER
3698 ; BUFFER         ( c −− a )                           Приписать буферу блок c и дать адрес буфера. Сам блок с диска не считывать.
3699 ; Резервирует блок в памяти и приписывает ему номер u (как и BLOCK ), но сам блок с диска не считывается.
3700 LBUFFE:         .DB     6
3701                 .TEXT   "BUFFER"                ; ( №блока −− 15096 ) F79
3702                 .DW     LFLUSH
3703 BUFFE:          .DW CALL, BUFN
3704                 .DW DUP,AT,ZLESS, ZBRAN,BR2     ; Буфер был изменён?
3705                 .DW BUFN,ONEP,CAT               ; Достать номер изменённого блока
3706                 .DW ZERO,RW                     ; Записать буфер на диск
3707 BR2:            .DW STORE                       ; Сохранить новый номер блока, сбросив флаг UPDATE
3708 BR3:            .DW LITP
3709                 .DB 3aH,0f8H                    ; rrDISKBUF  15096 = 3af8
3710 REXIT:          .DW EXIT
3711 
3712 ;#BLOCK
3713 ; BLOCK          ( c −− a )                           Убедиться, что в буфере блок c. Дать адрес буфера.
3714 ; Записывает в стек адрес первого байта в буфере с блоком, номером которого u.
3715 ; Если блок не находится в памяти, он переносится с носителя в буфер.
3716 ; Если блок, занимавший буфер, был ранее изменён (UPDATE), то этот блок сначала записывается на диск,
3717 ; и только затем на его место будет занесён новый блок.
3718 LBLOCK:         .DB     5
3719                 .TEXT   "BLOCK"                 ; ( №блока −− 15096 ) F79
3720                 .DW     LBUFFE
3721 BLOCK:          .DW CALL, TOR                   ; Сохраним номер запрошенного блока на вершине стека возвратов
3722                 .DW BUFN,ONEP,CAT               ; Какой у нас сейчас блок в буфере?
3723                 .DW I,SUB, ZBRAN,BLC            ; Блок уже считан? Вернуть адрес буфера.
3724                 .DW I, BUFN                     ; rrBUFBLK  15094 = 3af6
3725                 .DW DUP,AT,ZLESS, ZBRAN,BL1     ; Блок изменялся?
3726                 .DW BUFN,ONEP,CAT, ZERO,RW      ; Записать старый блок
3727 BL1:            .DW I,ONE,RW                    ; Считать запрошенный блок
3728                 .DW STORE                       ; Сохранить новый номер блока, заодно сбросив флаг UPDATE
3729 BLC:            .DW LEV                         ; Удалим номер блока со стека возвратов
3730                 .DW BRAN,BR3                    ; Вернём наш фиксированный адрес
3731 
3732 ;#xLINE
3733 ; (LINE)         ( c1 c2 −− a c )                     Вернуть адрес и длину строки c1 с экрана c2.
3734 ; Выдаёт адрес a и длину c строки c1 с экрана c2.
3735 ; В Каллисто выдаёт длину без пробелов в конце.
3736 LDLINE:         .DB     6
3737                 .TEXT   "(LINE)"                ; ( №строки №экрана −− адрес длина) К
3738                 .DW     LBLOCK
3739 DLINE:          .DW CALL
3740 RDLINE:         .DW TOR, LIT64,B3BUF,SSMOD, FROMR,PLUS, BLOCK
3741                 .DW PLUS, LIT64, DTRAI, EXIT
3742 
3743 
3744 ;#MESSAGE
3745 ; MESSAGE        ( c −− )                             Сохранить >IN в R# и вывести сообщение номер c.
3746 ; Если доработать до Форта−2012, можно приблизить к стр.99
3747 ; В Форте ИТЭФ сообщения при WARNING=1 хранились в блоке №4.
3748 ;
3749 ; Сообщение             Значения                        Сообщение при WARNING=1         Кто генерирует ошибку
3750 ;                                                       ( нет в Каллисто 1.0)
3751 ;
3752 ; MSG# 0        Слово не узнано.                                                        [COMPILE] TO 'N '
3753 ;               Число не узнано.
3754 ;               Нет соответствия системе счисления
3755 ; MSG# 1        Попытка извлечь нечто из пустого стека  EMPTY STACK                     ?STACK
3756 ; MSG# 2        Переполнение стека или словаря          STACK OR DIRECTORY OVERFLOW     ?STACK
3757 ; MSG# 4        Повторное описание слова (не является   IT ISN'T UNIQUE
3758 ;               фатальной ошибкой)
3759 ; MSG# 17       Используется только при компиляции      COMPILATION ONLY                ?COMP
3760 ; MSG# 18       Используется только при исполнении      EXECUTION ONLY                  ?EXEC
3761 ; MSG# 19       IF и THEN или другие операторы          CONDITIONALS AREN'T PAIRED      ?PAIRS
3762 ;               не имеют пары
3763 ; MSG# 20       Определение не завершено                DEFINITION ISN'T FINISHED       ?CSP
3764 ; MSG# 21       Неверный аргумент слова FORGET          PROTECTED DIRECTORY             FORGET ( нет в Каллисто 1.0)
3765 ;               Слово в защищённой области словаря
3766 ; MSG# 22       Должно использоваться только            USED AT LOADING ONLY            ?LOADING
3767 ;               при загрузке
3768 ; MSG# 23       Строка экрана вне диапазона 0..47
3769 ; MSG# 24       ???                                     ???                             FORGET ( нет в Каллисто 1.0)
3770 ; MSG# 26       Деление на 0                            0 DIVISION                      ( нет в Каллисто)
3771 ; (только в Каллисто, ошибки дисковых операций)
3772 ; MSG# 39       Сохранение от другой версии                                             LOAD" ( не может быть игнорировано)
3773 ; MSG# 40       Файл не найден                                                          LOAD"
3774 ; MSG# 41       Нет диска
3775 ; MSG# 42       Диск не форматирован
3776 ; MSG# 43       Нет места на диске
3777 ; MSG# 44       Нет места в каталоге
3778 ; MSG# 45       Ошибка имени файла/каталога
3779 ; MSG# 46       Невозможно удалить/создать/загрузить файл/каталог
3780 ; MSG# 47       Файл/каталог не выбран
3781 ; MSG# 48       Нет разрешения дисковой операции
3782 ;
3783 LMESS:          .DB     7
3784                 .TEXT   "MESSAGE"
3785                 .DW     LDLINE
3786 MESS:           .DW CALL, IN,UAT, RNUM,STORE, PDOTQ
3787                 .DB 5
3788                 .TEXT "MSG #"
3789                 .DW BRAN,RDOT
3790 
3791 ;#LOAD
3792 ; LOAD           ( c −− )                             Загрузить с диска и интерпретировать экран номер c.
3793 ; Загружает экран с номером u (компилирует или исполняет)
3794 LLOAD:          .DB     4
3795                 .TEXT   "LOAD"                  ; ( i∗x №блока −− j∗x) F79
3796                 .DW     LMESS
3797 LOAD:           .DW CALL, BLK,UAT,TOR, IN,UAT,TOR, IN,ZSTORE, BLK,STORE
3798                 .DW INTER, FROMR,IN,STORE, FROMR,BLK,STORE, EXIT
3799 
3800 ;#bs
3801 ; \              ( −− )                               Комментарий. Игнорировать остаток строки.
3802 ; Закончить интерпретацию строки.
3803 ; Также сделать, чтобы сбрасывала ввод с клавиатуры, это требования Форта−2012.
3804 LBSLASH:        .DB     81H,92                  ; "\" ( −− ) F83
3805                 .DW     LLOAD
3806 BSLASH:         .DW CALL, IN,UAT, LIT64,SLASH, ONEP, LIT64,STAR, IN,STORE, EXIT
3807 
3808 ;#bsS
3809 ; \S             ( −− )                               Комментарий. Игнорировать остаток экрана.
3810 ; Закончить интерпретацию экрана.
3811 LSEMIS:         .DB     82H,92                  ; "\S" ( −− ) Броуди
3812                 .TEXT   "S"
3813                 .DW     LBSLASH
3814 SEMIS:          .DW CALL, QLOAD, LEV, EXIT      ; Выход из INTERPRET в LOAD
3815 
3816 ;#THRU
3817 ; THRU           ( c1 c2 −− )                         Интерпретировать экраны с номерами от c1 до c2 включительно.
3818 ; Интерпретировать экраны от N1 до N2 включительно
3819 LTHRU:          .DB     4
3820                 .TEXT   "THRU"                  ; ( N1 N2 −− )
3821                 .DW     LSEMIS
3822 THRU:           .DW CALL, OVER,SUB,ONEP, XFOR,THRU2
3823 THRU1:          .DW DUP,LOAD, ONEP, XNEXT,THRU1 ; Можно ли вообще без LOAD, опираясь на реализацию \0 ?
3824 THRU2:          .DW DROP, EXIT
3825 
3826 ;#Arrow
3827 ; −−>            ( −− )                               Интерпретировать следующий экран.
3828 ; Даёт команду немедленно приступить к интерпретации следующего по порядку экрана.
3829 ; Может быть использовано внутри определения через двоеточие, которое пересекает границу блока.
3830 ; Проверить!
3831 ; Броуди рекомендует вместо −−> использовать THRU
3832 ; +++ Может убрать её нафиг?
3833 LARROW:         .DB     83H
3834                 .TEXT   "−−>"                   ; ( −−) F79
3835                 .DW     LTHRU
3836 ARROW:          .DW CALL, QLOAD, IN,ZSTORE, ONE,BLK,PSTOR, EXIT
3837 
3838 ;#TickN
3839 ; 'N             ( −− a )                             Ищет следующее слово, возвращая его NFA.
3840 ; Ищет слово с именем XXX (из входного или экранного буфера) в словаре Форта.
3841 ; Если поиск увенчался успехом, адрес поля имени (NFA, а не PFA) XXX будет записан в стек
3842 ; При неудачном поиске выдаётся сообщение об ошибке.
3843 ; Проверить и переделать под Форт−2012.
3844 LTICK:          .DB     2
3845                 .TEXT   "'N"                    ; ( −− nfa ) К
3846                 .DW     LARROW
3847 TICK:           .DW CALL, BL,WORD, FIND, ZEQU, ZERO,QERR, DROP, EXIT
3848 
3849 ;#Tick
3850 ; '              ( −− т )                             Дать токен (CFA) следующего слова.
3851 ; Настоящий ' из Форта реализован поверх каллистянского 'N
3852 LTICKCFA:       .DB     1
3853                 .TEXT   "'"                     ; ( −− cfa )
3854                 .DW     LTICK
3855 TICKCFA:        .DW CALL, TICK,BRAN,RN2CFA
3856 
3857 ;#BracketTick
3858 ; [']            ( −− | т )                           Скомпилировать токен (CFA) следующего слова, как числовой литерал.
3859 ; Используется только в определении через двоеточие. Компиляция адреса (CFA) следующего слова из определения как литерала.
3860 ; Проверить!
3861 LBTICK:         .DB     83H
3862                 .TEXT   "[']"                                   ; состояние компиляции: ( "<пр>имя" −− ) F83
3863                 .DW     LTICKCFA                                ; состояние выполнения: ( −− cfa )
3864 BTICK:          .DW CALL, TICKCFA, BRAN,RLITER
3865 
3866 ; Здесь было слово FORGET, которое не вместилось в Каллисто 1.0
3867 ; Оно удаляет из словаря, начиная с конца, все слова вплоть до XXX включительно.
3868 ; Правильная реализация (стр. 65, 151) проходится по наборам слов и урезает их.
3869 ; А ещё лучше сделать маркеры.
3870 
3871 ;#FwdMARK
3872 ; >MARK          ( −− a )                             Отметить текущий адрес для ссылки вперёд.
3873 LGMARK:         .DB     5
3874                 .TEXT   ">MARK"
3875                 .DW     LBTICK
3876 GMARK:          .DW CALL, HERE, ZERO, BRAN,RCOMMA
3877 
3878 ;#FwdRESOLVE
3879 ; >RESOLVE       ( a −− )                             Разрешить ссылку вперёд в адресе a.
3880 LGRESOLVE:      .DB     8
3881                 .TEXT   ">RESOLVE"
3882                 .DW     LGMARK
3883 GRESOLVE:       .DW CALL
3884 RGRESOLVE:      .DW HERE, LITP
3885                 .DB 39,17                       ; 10001
3886                 .DW SUB, SWAP, STORE, EXIT
3887 
3888 ;#BkwMARK
3889 ; <MARK          ( −− a )                             Отметить текущий адрес для ссылки назад.
3890 LLMARK:         .DB     5
3891                 .TEXT   "<MARK"
3892                 .DW     LGRESOLVE
3893 LMARK:          .DW CALL, HERE, EXIT
3894 
3895 ;#BkwRESOLVE
3896 ; <RESOLVE       ( a −− )                             Разрешить ссылку назад в адрес a.
3897 ;Отметить текущий адрес для ссылки назад.
3898 LLRESOLVE:      .DB     8
3899                 .TEXT   "<RESOLVE"
3900                 .DW     LLMARK
3901 LRESOLVE:       .DW CALL
3902 RLRESOLVE:      .DW LITP
3903                 .DB 39,17                       ; 10001
3904                 .DW SUB, BRAN,RCOMMA
3905 
3906 ;#UNTIL
3907 ; UNTIL          ( ф −− )                             Если ф ложен (ф=0), повторить цикл BEGIN UNTIL ещё раз.
3908 ; Конец цикла "BEGIN UNTIL".
3909 LUNTIL:         .DB     85H
3910                 .TEXT   "UNTIL"                 ; ( флаг −− ) F79 F84
3911                 .DW     LLRESOLVE
3912 UNTIL:          .DW CALL, ONE,QPAIR, COMP,ZBRAND, BRAN,RLRESOLVE
3913 
3914 ;#AGAIN
3915 ; AGAIN          ( −− )                               Перейти к началу бесконечого цикла BEGIN AGAIN.
3916 ; Конец бесконечного цикла "BEGIN AGAIN".
3917 LAGAIN:         .DB     85H
3918                 .TEXT   "AGAIN"                 ; F79
3919                 .DW     LUNTIL
3920 AGAIN:          .DW CALL, ONE,QPAIR, COMP,BRAND, BRAN,RLRESOLVE
3921 
3922 ;#BEGIN
3923 ; BEGIN          ( −− )                               Отметить начало циклов BEGIN.
3924 ; Начало циклов "BEGIN".
3925 LBEGIN:         .DB     85H
3926                 .TEXT   "BEGIN"                 ; F79
3927                 .DW     LAGAIN
3928 BEGIN:          .DW CALL, QCOMP, LMARK, ONE, EXIT
3929 
3930 ;#THEN
3931 ; THEN           ( −− )                               Конец ветвления IF.
3932 ; Конец ветвления "IF".
3933 LTHEN:          .DB     84H
3934                 .TEXT   "THEN"                  ; F79 F84
3935                 .DW     LBEGIN
3936 THEN:           .DW CALL
3937 RTHEN:          .DW TWO,QPAIR, BRAN,RGRESOLVE
3938 
3939 ;#ELSE
3940 ; ELSE           ( −− )                               Начало второй ветви ветвления IF.
3941 LELSE:          .DB     84H
3942                 .TEXT   "ELSE"                  ; F79
3943                 .DW     LTHEN
3944 SELSE:          .DW CALL, TWO,QPAIR, COMP,BRAND, GMARK, SWAP, GRESOLVE, TWO, EXIT
3945 
3946 ;#IF
3947 ; IF             ( ф −− )                             Если ф ложен (ф=0), перейти к парному THEN (или ELSE).
3948 ; Начало ветвления "IF".
3949 LIF:            .DB     82H
3950                 .TEXT   "IF"                    ; ( флаг −−) F79
3951                 .DW     LELSE
3952 SIF:            .DW CALL
3953 RIF:            .DW QCOMP, COMP,ZBRAND, GMARK, TWO, EXIT
3954 
3955 ;#WHILE
3956 ; WHILE          ( ф −− )                             Если ф ложен (ф=0), выйти из цикла BEGIN WHILE REPEAT.
3957 ; Ветвление "WHILE" в цикле "BEGIN WHILE REPEAT".
3958 LWHILE:         .DB     85H
3959                 .TEXT   "WHILE"                 ; ( флаг −−) F79
3960                 .DW     LIF
3961 WHILE:          .DW CALL, ONE,QPAIR, ONE, BRAN,RIF
3962 
3963 ;#REPEAT
3964 ; REPEAT         ( −− )                               Вернуться к самому началу цикла BEGIN WHILE REPEAT.
3965 ; Конец цикла "BEGIN WHILE REPEAT".
3966 LREPEAT:        .DB     86H
3967                 .TEXT   "REPEAT"                ; F79
3968                 .DW     LWHILE
3969 REPEAT:         .DW CALL, TOR,TOR, AGAIN, FROMR,FROMR, BRAN,RTHEN
3970 
3971 ;#FOR
3972 ; FOR            ( n −− )                             Повторить n раз цикл FOR NEXT. Если n<=0, цикл не исполнять.
3973 ; Начало цикла со счётчиком. Цикл исполнится ровно n раз, если n положительное число.
3974 ; Ситуация не определена, если n>65535.
3975 LFOR:           .DB     83H
3976                 .TEXT   "FOR"                   ; ( сколько −− ) К
3977                 .DW     LREPEAT
3978 FOR:            .DW CALL, QCOMP, COMP,XFORD, GMARK, LMARK, PI, EXIT
3979 
 ;#NEXT
3981 ; NEXT           ( −− )                               Вернуться к FOR, если ещё остались повторения цикла.
3982 ; Конец цикла со счётчиком.
3983 LSNEXT:         .DB     84H
3984                 .TEXT   "NEXT"                  ; К
3985                 .DW     LFOR
3986 SNEXT:          .DW CALL, PI,QPAIR, COMP,XNEXTD, LRESOLVE, BRAN,RGRESOLVE
3987 
3988 ;#SPACES
3989 ; SPACES         ( b −− )                             Если b>0, отобразить b пробелов шириной в цифру.
3990 ; Вывод заданного числа "цифровых" (шириной в цифру) пробелов на экран.
3991 ; Предусмотреть, если будет шрифт, отличный от шрифта 0.
3992 LSPACS:         .DB     6
3993                 .TEXT   "SPACES"                ; ( сколько −− ) F79
3994                 .DW     LSNEXT
3995 SPACS:          .DW CALL, LITB
3996                 .DB 31
3997                 .DW SWAP, EMI, EXIT
3998 
3999 ;#Unum−end
4000 ; U#>            ( x −− a c )                         Завершить преобразование целого числа. Дать адрес и длину для TYPE.
4001 ; В стеке остаётся число полученных символов и адрес, как это требуется для оператора TYPE
4002 ; Слово присутствует ещё в Форте−79, но у нас x помещается в одной ячейке стека.
4003 LEDIGS:         .DB     3
4004                 .TEXT   "U#>"                   ; ( x −− PAD длина ) K
4005                 .DW     LSPACS
4006 EDIGS:          .DW CALL
4007 REDIGS:         .DW DROP, HLD,UAT, PAD,OVER,SUB, EXIT
4008 
4009 ;#Fnum−end
4010 ; F#>            ( −− a c )                           Завершить преобразование десятичного числа. Дать адрес и длину для TYPE.
4011 ; Завершает преобразование числа с плавающей запятой
4012 ; В стеке остаётся число полученных символов и адрес, как это требуется для оператора TYPE
4013 LEFDIGS:        .DB     3
4014                 .TEXT   "F#>"                   ; ( −− HERE длина ) K
4015                 .DW     LEDIGS
4016 EDIGF:          .DW CALL
4017 REFDIGS:        .DW HERE, HLD,UAT, OVER, SUB, EXIT
4018 
4019 ;#SIGN
4020 ; SIGN           ( x −− )                             Добавить к форматной строке литеру '−', если число x отрицательно.
4021 ; Вводит знак "минус" в выходной буфер PAD, если x<0
4022 ; В соответствии со стандартом Форт−79, берёт свой аргумент с вершины стека.
4023 ; Обычно используется непосредственно после U#S
4024 LSIGN:          .DB     4
4025                 .TEXT   "SIGN"                  ; F79 F83
4026                 .DW     LEFDIGS
4027 SIGN:           .DW CALL, ZLESS, ZBRAN,SIG, LITB
4028                 .DB 45
4029                 .DW HOLD
4030 SIG:            .DW EXIT
4031 
4032 ;#Unum
4033 ; U#             ( u −− u1 )                          Преобразовать в литеру последнюю цифру из u и добавить к форматной строке.
4034 ; Преобразует одну цифру целого числа и записывает её в выходной буфер PAD
4035 ; Выдаёт цифру всегда, если преобразовывать нечего, записывается 0
4036 ; Слово присутствует ещё в Форте−79, но у нас оно оперирует лишь с одной ячейкой на вершине стека.
4037 LDIG:           .DB     2
4038                 .TEXT   "U#"                    ; ( x1 −− x2) К
4039                 .DW     LSIGN
4040 DIG:            .DW CALL, BASE,UAT, SLMOD, SWAP, LITB
4041                 .DB 9
4042                 .DW OVER, LESS, ZBRAN,DIGI, LITB
4043                 .DB 39                          ; 7 если заглавные буквы
4044                 .DW PLUS
4045 DIGI:           .DW LITB
4046                 .DB 48
4047                 .DW PLUS, HOLD, EXIT
4048 
4049 ;#UnumS
4050 ; U#S            ( u −− 0 )                           Выделять цифры числа u словом U# до получения нуля.
4051 ; Преобразует целое число до тех пор, пока не будет получен 0
4052 ; Одна цифра выдаётся в любом случае (0)
4053 ; Слово присутствует ещё в Форте−79, но у нас оно оперирует лишь с одной ячейкой на вершине стека.
4054 LDIGS:          .DB     3
4055                 .TEXT   "U#S"                   ; ( целое −− 0) К
4056                 .DW     LDIG
4057 DIGS:           .DW CALL
4058 DIS:            .DW DIG, DUP,ZEQU, ZBRAN,DIS, EXIT
4059 
4060 ;#num−start
4061 ; <#             ( −− )                               Начать форматное преобразование целого числа.
4062 ; Начинает процесс преобразования целого числа в последовательность кодов символов.
4063 LBDIGS:         .DB     2
4064                 .TEXT   "<#"                    ; F79
4065                 .DW     LDIGS
4066 BDIGS:          .DW CALL, PAD,HLD,STORE, EXIT
4067 
4068 ;#Fnum−start
4069 ; <F#            ( −− )                               Начать форматное преобразование десятичного числа.
4070 ; Начинает процесс преобразования числа с плавающей запятой в последовательность кодов символов.
4071 LBFDIGS:        .DB     3
4072                 .TEXT   "<F#"                   ; F79
4073                 .DW     LBDIGS
4074 BDIGF:          .DW CALL, HERE,HLD,STORE, EXIT  ; +++ Можно сократить на 2 байта с помощью BRAN
4075 
4076 ;#xd
4077 ; (.)            ( n −− a c )                         Преобразовать целое n в строку, вернуть её адрес и длину.
4078 ; Как I. −− только не выводит число на экран, а возвращает нам адрес и длину строки.
4079 LXDOT:          .DB     3
4080                 .TEXT   "(.)"                   ; ( целое −− адрес длина ) Форт ЕС
4081                 .DW     LBFDIGS
4082 XDOT:           .DW CALL, INT, DUP,ABS, BDIGS, DIGS, SWAP,SIGN, BRAN,REDIGS
4083 
4084 ;#Id
4085 ; I.             ( n −− )                             Вывести целое n на индикатор и дать пробел.
4086 ; Удаляет целое число из стека, преобразует и отображает его на экране с учётом записанной в BASE системы счисления.
4087 ; Традиционое слово Форта "." (F79 F12) получило новое имя, т.к. работает лишь с десятичными 12−разрядными целыми.
4088 LDOT:           .DB     2
4089                 .TEXT   "I."                    ; ( целое −− ) К
4090                 .DW     LXDOT
4091 DOT:            .DW CALL
4092 RDOT:           .DW XDOT, TYPE, BRAN,RSPACE
4093 
4094 ;#d
4095 ; .              ( x −− )                             Вывод x на индикатор и дать пробел. Совпадает с I. если BASE<>10.
4096 ; Вывод числа, за которым следует один пробел.
4097 ; Слово очень напоминает . −− бывшую ещё в Форте−79.
4098 ; Но наше слово . работает с плавучкой и создана на основе функций МК−161.
4099 LFDOT:          .DB     1
4100                 .TEXT   "."                     ; ( число −− ) К
4101                 .DW     LDOT
4102 FDOT:           .DW CALL
4103 RFDOT:          .DW BASE,UAT, LITB
4104                 .DB 10
4105                 .DW EQUAL,ZBRAN,RDOT    ; Если система счисления не десятичная, вывести целую часть числа
4106                 .DW BDIGF, DIGF, EDIGF, TYPE, BRAN,RSPACE
4107 
4108 ;#IdR
4109 ; I.R            ( n c −− )                           Вывести целое n на индикатор в поле длиной c справа.
4110 ; Печатает целое число n так, что младшая цифра занимает самое правое положение в выделенном поле, заданном числом u.
4111 ; Можно доработать до выравнивания по левому краю при отрицательных u.
4112 ; В Форте это слово называется .R F12
4113 LDOTR:          .DB     3
4114                 .TEXT   "I.R"                   ; ( целое поле −− ) К
4115                 .DW     LFDOT
4116 DOTR:           .DW CALL, TOR,XDOT,FROMR, OVER,SUB,SPACS, TYPE, EXIT
4117 
4118 ;               ∗∗ Вспомогательные процедуры ∗∗
4119 
4120 ;#q
4121 ; ?              ( a −− )                             Вывести значение ячейки по адресу a.
4122 ; Удаляет число из стека и отображает на экране содержимое ячейки, адрес которой равен этому числу.
4123 ; В Каллисто может выдать число с плавающей запятой, если оно считывается из десятичного регистра.
4124 ; Слово было в Форте−79, убрано из Форта−84.  Очень удобно для отладки.
4125 LQUEST:         .DB     1
4126                 .TEXT   "?"                     ; ( адрес −− ) F79
4127                 .DW     LDOTR
4128 QUEST:          .DW CALL, AT, BRAN,RFDOT
4129 
4130 ;#LIST
4131 ; LIST           ( c −− )                             Вывести экран c и записать его номер в переменную SCR.
4132 ; Распечатывает экран с номером u и записывает его номер в переменную SCR
4133 ; Доработать под нужды МК−161.
4134 ; Установить DECIMAL на время вывода номера экрана, тоже самое в MESSAGE
4135 USLIST:         .DB     4
4136                 .TEXT   "LIST"                          ; ( №блока −−) F79
4137                 .DW     LQUEST
4138 SLIST:          .DW CALL, DEC, DUP,SCR,STORE, PDOTQ     ; Без CR, экономим одну строчку.
4139                 .DB 3
4140                 .TEXT "S #"
4141                 .DW DOT, ZERO, LITB
4142                 .DB 48
4143                 .DW XFOR,LST1
4144 LSTI:           .DW CR, DUP,TWO,DOTR, SPACE, QBREAK
4145                 .DW DUP, SCR,UAT, DLINE, TYPE1, ONEP, XNEXT,LSTI
4146 LST1:           .DW DROP, EXIT
4147 
4148 ;#INDEX
4149 ; INDEX          ( c1 c2 −− )                         Вывести начальную строку экранов с номерами от c1 до c2.
4150 ; Выводит на экран верхние строчки экранов, номера блоков которых входят в диапазон от нач до кон.
4151 ; Для экономии места на индикаторе после номера экрана пробела нет.
4152 LINDEX:         .DB     5
4153                 .TEXT   "INDEX"                 ; ( нач кон −−) F79
4154                 .DW     USLIST
4155 INDEX:          .DW CALL, OVER,SUB,ONEP, XFOR,INDX2
4156 INDX:           .DW CR, DUP, LITB
4157                 .DB 3
4158                 .DW DOTR, QBREAK, ZERO, OVER, DLINE, TYPE1
4159                 .DW ONEP, XNEXT,INDX
4160 INDX2:          .DW DROP, EXIT
4161 
4162 ;#WORDS
4163 ; WORDS          ( −− )                               Слова. Отобразить имена всех слов контекста.
4164 ; Просмотр полного списка операторов, хранящихся в данный момент в контексте
4165 LWORDS:         .DB     5
4166                 .TEXT   "WORDS"                 ; ( −− )
4167                 .DW     LINDEX
4168 WORDS:          .DW CALL, CONT,UAT,UAT
4169 W1:             .DW DUP,IDDOT, QBREAK, NLINK,UAT, DUP,ZEQU, ZBRAN,W1, DROP, EXIT
4170 
4171 ;#COPY
4172 ; COPY           ( c1 c2 −− )                         Копировать блок c1 в блок c2.
4173 ; n m COPY копирует экран n на экран m
4174 ; Часто это команда редактора, но её удобно иметь в ядре.
4175 ; +++ Почему−то зависает в эмуляторе!
4176 LCOPY:          .DB     4
4177                 .TEXT   "COPY"                  ; ( откуда куда −− ) Форт ИТЭФ
4178                 .DW     LWORDS
4179 COPY:           .DW CALL, SWAP, BLOCK,TWOM,STORE, UPDAT, BRAN,RFLUSH
4180 
4181 ;#DEPTH
4182 ; DEPTH          ( −− U )                             Глубина стека. U −− количество значений на стеке данных до исполнения DEPTH.
4183 ; Выдаёт в стек полное количество чисел, хранившихся в стеке до исполнения этой команды.
4184 LDEPTH:         .DB     5
4185                 .TEXT   "DEPTH"                 ; ( −− глубина ) F79
4186                 .DW     LCOPY
4187 DEPTH:          .DW CALL, SZERO,UAT, SPAT,ONEP,SUB, EXIT
4188 
4189 ;#DUMP
4190 ; DUMP           ( a c −− )                           Дамп. Вывести c байт от адреса a.
4191 ; Отображает u байт памяти начиная с адреса a
4192 ; Можно сделать роскошный дамп, но пока так.
4193 LDUMP:          .DB     4
4194                 .TEXT   "DUMP"                  ; ( адрес длина −− ) F79
4195                 .DW     LDEPTH
4196 DUMP:           .DW CALL, DUP2, XFOR,DP1E
4197 DP1:            .DW DUP,CAT,FDOT, ONEP, XNEXT,DP1
4198 DP1E:           .DW DROP, XFOR,DP2E
4199 DP2:            .DW DUP,CAT,CDOT, ONEP, XNEXT,DP2
4200 DP2E:           .DW DROP, BRAN,RSPACE           ; +++ Нужен ли пробел или ok уже даёт его?
4201 
4202 ;#dS
4203 ; .S             ( −− )                               Печать стека, вершина справа.
4204 ; Распечатка всего содержимого стека, оставляющая стек без изменений.
4205 ; Подумать, может быть можно сократить через цикл с отрицательным приращением.
4206 LSTY:           .DB     2
4207                 .TEXT   ".S"                    ; ( −− ) F12
4208                 .DW     LDUMP
4209 STY:            .DW CALL, DEPTH, XFOR,STY3
4210 STY1:           .DW I,ONEM,PICK, FDOT, XNEXT,STY1
4211 STY3:           .DW EXIT
4212 
4213 ;#kaate
4214 ; КАТ            ( c −− )                             Перейти в каталог Каллисто с литерой c. Если каталога нет, создать его.
4215 ; Поиск каталога Каллисто и переход в него.  Если каталога нет, он создаётся.
4216 ; c −− код символа, стоящего сразу после "Каллисто" в названии каталога
4217 LKATALOG:       .DB     3
4218                 .TEXT   "КАТ"                   ; ( c −− ) К
4219                 .DW     LSTY
4220 KAT:            .DW     JKAT
4221 JKAT:           PKRM03 MB RM3 1 + M3            ; RB := символ
4222                 9034 MA
4223 KATRY:          1 PPM 9120                      ; Разрешить дисковые операции
4224                 Cx
4225 ;               PPM 9128 PPM 9121               ; 1. Выбор корневого каталога диска A
4226                 M8                              ; Начинаем с нулевой строки каталога
4227 KATNEXT1:       Cx PPM 9030 PPM 9031            ; Сброс индексных регистров 0 и 1
4228                 RM8 PPM 9122                    ; 2. Чтение строки каталога
4229                 KRMA 2 − PX=0 KATNEXT           ; 4. Подкаталог?
4230                 KRMA KRMA                       ; Пропустить поле 2  ( номер кластера, 2 байта)
4231                 .NUM strForthM1
4232                 M5 8 M0
4233 KATZ:           RM5 1 + M5 KPRGM KRMA − PX=0 KATNEXT
4234                 FL0 KATZ
4235                 KRMA RMB − FX=0 KATNEXT         ; 5. Имя начинается с "Каллисто" и переданного символа?
4236                 RM8 PPM9128 PPM 9123            ; 6. Перейти в каталог, всё
4237                 KGOTO9                          ; NEXT
4238 KATNEXT:                                        ;−−− Перейти к следующей строке каталога
4239                 RM8 1 + M8  64 − PX=0 KATNEXT1  ; Просмотрели весь каталог?
4240                 PPM 9030 3 PPM 9031             ; Настройка индексных регистров 0 и 1
4241                 .NUM strForthM1
4242                 M5 8 M0
4243 KATW:           RM5 1 + M5 KPRGM KMA FL0 KATW   ; 7. Загрузить строку "Каллисто" в буфер
4244                 RMB KMA
4245                 11 M0 32
4246 KATQ:           KMA FL0 KATQ                    ; Заполняем пробелами неиспользуемый конец поля имени
4247                 2 PPM 9128 PPM 9125             ; 8. Создание каталога
4248                 PGOTO KATRY                     ; Переходим в него
4249 
4250 ;#SAVE−TEXT
4251 ; SAVE−TEXT      ( −− )                               Сохранить область текста в энергонезависимую память.
4252 LSAVETEXT:      .DB     9
4253                 .TEXT   "SAVE−TEXT"
4254                 .DW     LKATALOG
4255 SAVETEXT:       .DW     JSAVETEXT
4256 JSAVETEXT:      5095 M5 3072 PGSB SMLM0
4257                 KGOTO9
4258 
4259 ;#veyerukael
4260 ; ВЫКЛ           ( −− )                               Сохранить сеанс работы в энергонезависимой памяти МК−161.
4261 ; Позволяет выключить на время МК−161, сохранив сеанс работы в энергонезависимой памяти
4262 ; Область текста не сохраняется, а флаг UPDATE и номер блока сбросятся при включении
4263 ; Поэтому нужно сперва делать FLUSH, а уже потом ВЫКЛ
4264 LWYKL:          .DB     4,130,155,138,139       ; "ВЫКЛ"
4265                 .DW     LSAVETEXT
4266 WYKL:           .DW     JWYKL
4267 JWYKL:          PGSB SVER MD                    ; После включения не стирать память, но экран очистить
4268                 GSB SAVDEC  GSB SAVBIN          ; Сохранить все десятичные и двоичные регистры в энергонезависимую память
4269                 8 PPM 9008                      ; Тёмный экран
4270                 4 PPM 9001                      ; Светлым
4271                 0 ENT 127 PPM 9000              ; Курсор в (0,127)
4272                 2 / <−> PPM 9012 KGRPH          ; Линию до (63,0)
4273 HALT:           GOTO HALT                       ; Бесконечный цикл в ожидании выключения питания
4274 
4275 ; Сохранить регистры в энергонезависимую память
4276 ; R1 = 4 для десятичных регистров (0..999), всего 1000
4277 ;    = 2 для двоичных регистров (1000..5095), всего 4096
4278 ;
4279 SAVRGS:         RM1 4 − FX=0 SAVBIN
4280 SAVDEC:         1 M5 998                        ; R0, R1 не сохранять
4281                 GOTO SMLM0
4282 SAVBIN:         999 M5 4096
4283 SMLM0:          M0
4284 SML:            1 PPM 9047
4285                 KRM5 PKM05
4286                 FL0 SML
4287                 RTN
4288 
4289 ;#RGRW
4290 ; RGRW           ( a c1 c2 −− c3 )                    Чтение/запись (c2=1/0) десятичного/двоичного (c1=4/2) файла с именем из 20 литер по адресу a+1.
4291 ; Следующие два слова можно разбить на подпрограммы и сэкономить кучу памяти
4292 ; Только делать это надо очень аккуратно, ведь все регистры запомнятся/изменятся
4293 ; По хорошему код надо переписать на высокий уровень такими полезными словами,
4294 ; которыми было бы удобно пользоваться и в других целях
4295 
4296 ; Чтение/запись файла регистров (словаря) в загруженном каталоге.
4297 ; Выдаёт код ошибки дисковой операции, 0 если успех.
4298 ; a − адрес имени файла, состоящего из 20 символов минус 1
4299 ; t − тип файла (t=4 для десятичных регистров, t=2 для двоичных регистров)
4300 ; flg − флаг "чтение−запись" (flg=1 для чтения, flg=0 для записи)
4301 ; f − статус операции (f=0 нет ошибок, f<>0 код ошибки дисковой операции)
4302 LRERW:          .DB     4
4303                 .TEXT   "RGRW"                  ; ( a t flg −− f ) К
4304                 .DW     LWYKL
4305 RERW:           .DW     JRERW
4306 JRERW:          PKRM03 MA                       ; RA := flg  ( флаг R/W)
4307                 RM3 1 + M8  1 + M7 1 + M3
4308                 KRM7 1 EE 4 − M7                ; R7 := a−1  ( адрес 20−символьного имени файла − 1)
4309                 KRM8 M1                         ; R1 := t    ( тип файла)
4310                 Cx M8 9034 MB                   ; R8 := 0    ( номер строки в каталоге) ; RB := 9034  ( чтение по индексу 1)
4311 RERWN1:         Cx PPM 9030 PPM 9031            ; Сброс индексных регистров 0 и 1
4312                 RM8 PPM 9122                    ; 1. Считать строчка каталога.
4313                 KRMB RM1 − FX=0 JZRERWNXT       ; 2. Нужный нам тип файла?
4314                 KRMB KRMB                       ; Пропустить поле 2 ( двухбайтовое)
4315                 RM7 M5 20 M0                    ; R5 := R7 ( адрес имени − 1)   ; R0 := 20  ( длина имени)
4316 RERWZ:          KRMB KRM5 −
4317 JZRERWNXT:      FX=0 RERWNXT
4318                 FL0 RERWZ                       ; 3. Совпадает имя в каталоге с нужным?
4319                 RMA FX!=0 RERWR1                ; 4. R/W ?
4320                 RMC                             ; Нужно только при чтении дестичных регистров
4321                 RM8 PPM 9128 PPM 9123           ; 5. Считать найденный файл
4322                 <−> MC                          ; Восстановление RC
4323                 256 ME
4324                 GOTO JRERWRET                   ; 6. Выход
4325 RERWR1:         PGSB SAVRGS
4326                 RM8 PPM 9128 PPM 9126           ; 7. Перезаписать найденный файл
4327 JRERWRET:       PGOTO RERWRET                   ; 8. Выход
4328 RERWNXT:        RM8 1 + M8 64 − PX=0 RERWN1     ; 9−10. Следующая строчка. Она 64−я?
4329                 RMA FX!=0 RERWR2                ; 12. R/W ?
4330                 4                               ; 13−14. Выход с кодом ошибки 40 "файл не найден".
4331 JZERO:          0 KM3 KGOTO9                    ; Обработчик FALSE
4332 RERWR2:         PPM 9030 3 PPM 9031             ; RX==0 ( инициализация индексных регистров)
4333                 RM7 M5 20 M0
4334 RERWS:          KRM5 KMB FL0 RERWS              ; 11. Записать имя файла в буфер.
4335                 PGSB SAVRGS
4336                 RM1 PPM 9128 PPM 9125           ; 15. Создать файл.
4337 RERWRET:        PPRM 9129 FX!=0 RERW0           ; 16. Выход
4338                 40 +                            ; Преобразовать код ошибки дисковой операции в код ошибки Каллисто
4339 RERW0:          KM3 KGOTO9
4340 
4341 ;#LORW
4342 ; LORW           ( a c −− )                           Чтение/запись (c=1/0) блока из файла с именем из 4 литер по адресу a.
4343 ; Чтение/запись блока из файла в загруженном каталоге.
4344 ; Если файла нет, он создаётся.
4345 ; a − адрес имени файла, состоящего из 4 символов
4346 ; flg − флаг "чтение−запись" (flg=1 для чтения, flg=0 для записи)
4347 LLORW:          .DB     4
4348                 .TEXT   "LORW"                  ; ( a flg −− ) К
4349                 .DW     LRERW
4350 LORW:           .DW     JLORW
4351 JLORW:          PKRM03 MA                       ; RA := flg  ( флаг R/W)
4352                 RM3 1 + M7 1 + M3
4353                 KRM7 10001 − M7                 ; R7 := a−1  ( адрес четырёхсимвольного имени файла − 1)
4354                 Cx M8 9034 MB                   ; R8 := 0    ( номер строки в каталоге) ; RB := 9034  ( чтение по индексу 1)
4355 LORWN1:         Cx PPM 9030 PPM 9031            ; Сброс индексных регистров 0 и 1
4356                 RM8 PPM 9122                    ; 1. Считать строчку каталога
4357                 KRMB 5 − PX=0 LORWNXT           ; 2. Это файл с текстом?
4358                 KRMB KRMB                       ; Пропустить поле 2  ( двухбайтовое)
4359                 RM7 M5 4 M0                     ; R5 := R7  ( адрес имени − 1)  ; R0 := 4  ( длина имени)
4360 LORWZ:          KRMB KRM5 − PX=0 LORWNXT        ; Символы равны?
4361                 FL0 LORWZ                       ; 3. Это Bxxx ? ( сравниваем 4 символа имени)
4362                 RMA PX!=0 LORWR1                ; 4. R/W ?
4363                 RM8 PPM 9128 PPM 9123           ; 5. Считать найденный файл
4364                 KGOTO9                          ; 6. NEXT
4365 LORWR1:                                         ;−−− Файл найден, записываем в него.
4366                 RM8 PPM 9128 PPM 9126           ; 8. Перезаписать найденный файл
4367                 KGOTO9                          ; 9. NEXT
4368 LORWNXT:        RM8 1 + M8 64 − PX=0 LORWN1     ;−−− Перейдём к следующей строчке каталога. Закончились?
4369                 RMA FX!=0 LORWR2                ; 10. R/W ?
4370                 5095 M5 3072 M0 32              ; R5 := 5095  ( начало текста − 1)      ; R0 := 3072  ( длина текста)
4371 LORWE:          KM5 FL0 LORWE                   ; 11. Заполнить текст пробелами −− переход на BLANK ?
4372                 KGOTO9                          ; 16. Всё, "считан" ещё не существующий блок. Сюрприз: он весь состоит из пробелов!
4373 LORWR2:                                         ;−−− Файл не найден, надо создать и записать текст в него.
4374                 PPM 9030 3 PPM 9031             ; Инициализация индексных регистров 0 и 1
4375                 RM7 M5 4 M0                     ; R5 := R7  ( адрес имени − 1)  ; R0 := 4  ( длина имени)
4376 LORWS:          KRM5 KMB FL0 LORWS              ; Переносим имя в буфер
4377                 16 M0 32                        ; R0 := 16  ( кол−во пробелов)  ; RX := 32 ( код пробела)
4378 LORWS1:         KMB FL0 LORWS1                  ; 12. Строка "Bxxx" ( 4 символа имени + 16 символов пробела)
4379                 5 PPM 9128 PPM 9125             ; 13. Создать файл ( записать текст)
4380                 KGOTO9                          ; 14. NEXT
4381 
4382 ;#lowRW
4383 ; _RW            ( c1 c2 −− )                         Чтение/запись (c2=1/0) блока номер c1.
4384 ; Слово _RW имеет своим прототипом R/W в Форте ИТЭФ, но у нас адрес известный и мы его как параметр не передаём.
4385 ; bl# − номер экрана (блока)
4386 ; flg − флаг "чтение−запись" (flg=1 для чтения, flg=0 для записи)
4387 LRW:            .DB     3
4388                 .TEXT   "_RW"                   ; ( bl# flg −− ) К
4389                 .DW     LLORW
4390 RW:             .DW CALL
4391 RRW:            .DW DUP,ZEQU, ZBRAN,RW1, SAVETEXT
4392 RW1:            .DW SWAP
4393                 .DW DUP,ONEM,ABS, LITB
4394                 .DB 60
4395                 .DW SLASH,LITB
4396                 .DB 3
4397                 .DW MIN,LITB
4398                 .DB 49
4399                 .DW PLUS,KAT                    ; Выбираем нужный каталог
4400                 .DW BDIGS, DIG,DIG,DIG, LITB
4401                 .DB 66
4402                 .DW HOLD, EDIGS, DROP           ; Генерируем имя файла "Bxxx"
4403                 .DW SWAP, LORW                  ; Читаем/записываем
4404                 .DW DISKOFF, EXIT               ; Запретить все дисковые операции
4405 
4406 ;#SAVEq
4407 ; SAVE"          ( −− )                               Сохранить сеанс в файлы D/B с именем, взятым из входного потока.
4408 ; Сохранить в файлы словарь Форта и десятичный словарь.
4409 LSAVEQ:         .DB     5
4410                 .TEXT   "SAVE"                          ; SAVE" ( "<пр>имя−файла" −− ) К
4411                 .DB     34                              ; '"'
4412                 .DW     LRW
4413 SAVEQ:          .DW CALL, FLUSH                         ; Сохранения с флагом UPDATE −− мина замедленного действия
4414                 .DW BL,KAT, LITB
4415                 .DB 34
4416                 .DW WORD, COUNT, UPPER                  ; Перейти в каталог "Каллисто ", получить имя файла.
4417                 .DW LITB
4418                 .DB 13                                  ; 13
4419                 .DW DUP,IP, SWAP                        ; ( 13 −− @R13,13)
4420                 .DW VERSION, OVER, RSTO                 ; ( @R13,13 −− @R13,13)
4421                 .DW HERE,LITB
4422                 .DB 4
4423                 .DW ZERO,RERW, DUP,QERR                 ; Записать десятичный файл.
4424                 .DW HERE,LITB
4425                 .DB 6
4426                 .DW ZERO,RERW, DUP,QERR                 ; Записать двоичный файл.
4427                 .DW RSTO, DISKOFF, EXIT                 ; Восстановить R13 и выйти.
4428 
4429 ;#LOADq
4430 ; LOAD"          ( −− )                               Восстановить сеанс из файлов D/B с именем, взятым из входного потока.
4431 ; Считать из файла словарь Форта и десятичный словарь.
4432 ; Также нужно слово для считывания/запуска словаря из шитого кода.
4433 ; QERR может привести к печальным последствиям
4434 ; Пока работает не надёжно, если файл другой версии.
4435 LLOADQ:         .DB     5
4436                 .TEXT   "LOAD"                  ; LOAD" ( "<пр>имя−файла" −− ) К
4437                 .DB     34                      ; '"'
4438                 .DW     LSAVEQ
4439 LOADQ:          .DW CALL, BL,KAT, LITB
4440                 .DB 34
4441                 .DW WORD, COUNT,UPPER
4442                 .DW HERE, LITB
4443                 .DB 4
4444                 .DW ONE,RERW, DUP,QERR
4445                 .DW ZERO,LITB
4446                 .DB 13                          ; 13
4447                 .DW DUP,IP, VERSION,SUB, ZBRAN,LQOK
4448 ;               .DW SPSTO, RPSTO
4449                 .DW LITB
4450                 .DB 39
4451                 .DW MESS, KEY, COLD             ; 39 Сохранение от другой версии
4452 LQOK:           .DW RSTO                        ; Обнуляем R13
4453                 .DW HERE,LITB
4454                 .DB 6
4455                 .DW ONE,RERW, DUP,QERR, PDOTQ
4456                 .DB 4
4457                 .TEXT " ok"                     ; Подделываем приглашение от QUIT
4458                 .DB 10
4459                 .DW BRAN,RWARM
4460 
4461                 .ENDP