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