; ; ╔═════════════════════════════════════════════════════════════════════════╗ ; ║ This file is generated by The Interactive Disassembler (IDA) ║ ; ║ Copyright (c) 2006 by DataRescue sa/nv, ║ ; ║ Licensed to: kon v palto ║ ; ╚═════════════════════════════════════════════════════════════════════════╝ ; ; Input MD5 : 5015228EEEB238E65DA8EDCD1B6DFAC7 ; File Name : DISK_327v12.ROM ; Format : Binary file ; Base Address: 0000h Range: E000h - F000h Loaded length: 1000h ; Processor: K1801VM1 ; Target assembler: Turbo8DK Assembler .LA 160000 ERRFDD=52 ;адрес, куда сохраняется номер ошибки ;ячейки рабочей области драйвера дисковода CSRW=0 ;копия по записи регистра состояния КНГМД CURTRK=2 ;адрес текущей дорожки (адрес одного из следующих байтов из таблицы) TRKTAB=4 ;таблица текущих дорожек TDOWN=10 ;задержка опускания головки TSTEP=12 ;задержка перехода с дорожки на дорожку TRKCOR=14 ;дорожка начала предкомпенсации BRETRY=15 ;число попыток повтора при ошибке FLAGS=16 ;рабочая ячейка драйвера FILLB=17 ;код заполнения при форматировании FLGPTR=20 ;указатель на байт признаков (адрес одного из следующих байтов из таблицы) FLGTAB=22 ;таблица признаков ADDR=26 ;адрес начала массива данных в ОЗУ (обязательно четный) WCNT=30 ;количество слов для пересылки SIDE=32 ;номер стороны диска TRK=33 ;номер дорожки UNIT=34 ;номер привода SECTOR=35 ;номер сектора WRTVAR=36 ;значение, записываемое при форматировании MARKER=40 ;буфер маркера при записи FREE=42 ;длина пустого остатка сектора INTIME=44 ;счетчик длительности индекса BUF4=46 ;буфер для сохранения вектора 4 BUFSP=50 ;буфер для сохранения SP BUFPSW=52 ;буфер для сохранения PSW CRETRY=54 ;счетчик повторов при ошибке TURNS=55 ;число оборотов диска при поиске сектора SECRET=56 ;число повторных попыток поиска сектора ERRNUM=57 ;буфер для номера ошибки MAXSEC=60 ;число секторов на дорожке HOLTIN=62 ;время задержки после индекса SECLEN=64 ;длина сектора в словах START: BR BOOT0 ; BOOT0: Автоматическая загрузка BR BOOT1 ; BOOT1: Загрузка с выбранного привода BR RWBLK ; RWBLK: Чтение-запись по номеру блока BR RWSEC ; RWSEC: Чтение-запись по номеру сектора BR INIT ; INIT: Инициализация рабочей области JMP FORMAT ; FORMAT: Форматирование дорожки JMP EXT ; EXT: Расширенная арифметика ; BOOT0: П/п автоматической загрузки BOOT0: CLR R0 ; Начнем с нулевого привода CALL BOOT ; Пробуем загрузиться, попутно выполняя инициализационные действия, с приводов 0,1,2 MOV #3, R0 ; Там не получилось, пробуем с привода 3 CALL BOOTC ; Загружаем стандартным загрузчиком BR STOP ; Загрузчика нигде нет, выходим ; BOOT1: Загрузка с выбранного привода BOOT1: NOP ; здесь была установка стека, NOP ; но ее занопили CALL BOOTC ; Загружаем стандартным загрузчиком ; STOP: Остановка двигателя НГМД и выход из п/п STOP: CLR (R3) CLR @#177130 CLR @#177660 RETURN ; BOOTC: П/п загрузки и запуска загрузчика BOOTC: JMP BOOTC$ ; пойдем, начальные действия, а потом продолжим с BOOTN .WORD 0 ; это сделано, чтобы создать точку входа в отладчик 1$: JMP DEBUGR ;идем отлаживать BR 1$ ;1600100 - точка входа в отладчик ; ─────────────────────────────────────────────────────────────────────────── ; продолжение загрузчика с дисковода BOOTN: CLR R0 ; Читаем нулевой блок MOV #400, R1 ; длиной 400 слов (1000 байт) MOV #1000, R2 ; с адреса 1000 CALL RWBLK ; Прочитаем... BCS 1$ ; не получилось - выход CMP @#1000, #240 ; Загрузчик начинается с NOP? BNE 1$ ; Нет - выход CMP @#1002, #5 ; Вторая команда - RESET? BEQ 1$ ; Да - диск не системный, выход MOVB UNIT(R3), R0 ; Вспомним, откуда это мы загрузились JMP RUNLDR ; пойдем запускать то, что загрузили 1$: RETURN ; Вот и все ; RWSEC: Чтение-запись по номеру сектора RWSEC: BR SUBRW ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; INIT: П/п инициализации рабочей области INIT: CLR (R3) ; Очистим копию РС КНГМД MOV #177777, TRKTAB(R3) ; Заполним таблицу текущих дорожек MOV #177777, TRKTAB+2(R3) MOV #10000., TDOWN(R3) ; Время опускания головки MOV #10000., TSTEP(R3) ; Время перехода дорожки MOVB #32., TRKCOR(R3) ; Дорожка начала предкомпенсации MOVB #20., BRETRY(R3) ; Число повторов при ошибке CLRB FLAGS(R3) ; Очистим флаги CLR FLGTAB(R3) ; и таблицу признаков CLR FLGTAB+2(R3) MOV #10., MAXSEC(R3) ; Занесем число секторов на дорожке = 10 RETURN ; End of function INIT ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; RWBLK: П/п чтения-записи по номеру блока ;Входные параметры: ;R0 - номер блока на диске ;R1 - длина пересылаемого массива в словах (>0 - чтение <0 - запись) ;R2 - начальный адрес массива в словах ;R3 - базовый адрес рабочей области драйвера ;UNIT - номер привода (0 - А) RWBLK: MOV #10, R4 MOVB MAXSEC(R3), R5 ; число секторов на дорожке SWAB R5 ; делим ст. байт R0 на ст. байт R5 1$: CMP R5, R0 BHI 2$ SUB R5, R0 SEC 2$: ROL R0 ; Частное накапливается в мл. байте R0 SOB R4, 1$ MOVB R0, R4 ; В мл. байте номер дорожки, сохраним его CLRB R0 SWAB R0 ; Получили остаток от деления INC R0 ; Номер сектора на единичку больше CLRB SIDE(R3) ; Пока мы на нижней стороне CMP R0, MAXSEC(R3) ; Этот сектор за пределами дорожки? BLE 3$ ; Нет - полный порядок INCB SIDE(R3) ; Да - значит он на другой стороне SUB MAXSEC(R3), R0 ; и номер у него другой 3$: CALL SETPTR ; Установим указатель на байт признаков BITB #2, @FLGPTR(R3) ; Диск односторонний? BEQ 4$ ; Нет - оставим все как есть ASRB SIDE(R3) ; Да - сторона всегда 0, ROLB R4 ; а дорожка уже другая 4$: MOVB R4, TRK(R3) ; Запишем результаты: дорожка, MOVB R0, SECTOR(R3) ; сектор, MOV R1, WCNT(R3) ; число слов, MOV R2, ADDR(R3) ; адрес буфера ; SUBRW: П/п чтения-записи по номеру сектора ;Входные параметры: ;R3 - базовый адрес рабочей области драйвера ;ADDR - начальный адрес массива в словах ;WCNT - длина пересылаемого массива в словах (>0 - чтение <0 - запись) ;SIDE - номер стороны (0-нижняя, 1- верхняя) ;TRK - номер должки ;UNIT - номер привода (0 - А) ;SECTOR - номер сектора (1-10.) SUBRW: MOV SP, BUFSP(R3) ; Сохраняем SP, MFPS BUFPSW(R3) ; PSW, MOV #4, R0 MOV (R0), BUF4(R3) ; @#4 в соответствующих буферах MOV PC, (R0) ; Переназначаем вектор @#4 на себя, ADD #VECT4-., (R0) ; теперь он равен VECT4 TSTB SECTOR(R3) ; Какой сектор нужен? BEQ ERR12 ; Нулевых у нас нет, ошибка 12 CMPB SECTOR(R3), MAXSEC(R3) ; Сектор больше максимального? BGT ERR12 ; Таких тоже нет, ошибка 12 CLRB @#ERRFDD ; Пока ошибок нет BICB #30, FLAGS(R3) ; Пока не записываем TST WCNT(R3) ; Сколько слов будем пересылать? BEQ QUIT ; 0 - ничего делать не надо, выход BPL READ ; больше 0 - будем читать ; П/п записи массива NEG WCNT(R3) ; Нормализуем длину BISB #20, FLAGS(R3) ; Установим признак записи MOV #175641, MARKER(R3) ; Запомним маркер данных BITB #1, FLAGS(R3) ; Записываем скрытно? BEQ 1$ ; Нет - идем дальше MOV #174241, MARKER(R3) ; Да - запомним специальный маркер 1$: CALL ENGINE ; ENGINE: Включим двигатель, запомним привод BIT #4, (R4) ; Защита записи есть? BEQ MAIN ; Нет - продолжить работу MOV #1, R0 ; Есть - ошибка 1 BR ERR ; ─────────────────────────────────────────────────────────────────────────── ; READ: П/п чтения массива READ: CALL ENGINE ; ENGINE: Включим двигатель, запомним привод MOV #400, SECLEN(R3) ; Сектора стандартной длины BITB #4, @FLGPTR(R3) ; А они ли нам нужны? BEQ MAIN ; Да - продолжить работу MOV #1000, SECLEN(R3) ; Нет - сектора удвоенной длины ; MAIN: Основная часть п/п чтения/записи массива MAIN: CALL GOTRK ; GOTRK: Позиционируем на нужную дорожку CALL CORR ; CORR: Включим схему предкоррекции MOVB BRETRY(R3), CRETRY(R3) ; Число попыток - в буфер ; Получение заголовка сектора MOVB #10, ERRNUM(R3) ; Предполагаем ошибку 10 MOVB #20., TURNS(R3) ; На операцию отводим 20. оборотов 1$: CALL FINDH ; FINDH: Найдем маркер заголовка BCC 2$ ; Нашли - идем дальше 3$: MOVB ERRNUM(R3), R0 ; Не нашли - выходим с ошибкой BR ERR ; ─────────────────────────────────────────────────────────────────────────── 2$: TSTB (R4) ; Информация получена? BPL 2$ ; Нет - подождем MOV (R5), R0 ; Теперь прочитаем ее 4$: TSTB (R4) ; Хотим еще информации! BPL 4$ ; Дождемся ее появления MOV (R5), R1 ; и заберем ее CALL TSTCRC ; TSTCRC: Проверим CRC BNE 5$ ; Ошибки нет - идем дальше MOVB #2, ERRNUM(R3) ; Ошибка 2! DECB CRETRY(R3) ; Остались еще попытки? BNE 1$ ; Да - повторим чтение BR 3$ ; Видно, не судьба! Выходим с ошибкой ; ─────────────────────────────────────────────────────────────────────────── 5$: BIC #177774, R1 ; Выделяем код длины сектора BITB #4, @FLGPTR(R3) ; Какие сектора нам были нужны? BNE LONG ; LONG: Длинные - посмотрим... CMPB R1, #2 ; Короткие. А на диске какие? BEQ WORK ; WORK: Тоже короткие. Порядок, идем дальше DECB CRETRY(R3) ; Как там дела с попытками? BNE 1$ ; Есть еще - повторим все сначала ; ERR12: Выход с ошибкой ERR12: MOV #12, R0 ; Выходим с ошибкой 12 ERR: JMP ERROR ; ERROR: Идем на обработку и выход ; ─────────────────────────────────────────────────────────────────────────── QUIT: JMP EXIT ; EXIT: Идем на нормальный выход ; ─────────────────────────────────────────────────────────────────────────── LONG: CMPB R1, #2 ; Нужны длинные сектора, а какие здесь? ; ОШИБКА: На самом деле код длинных секторов (1024 байта на сектор) - 3, а не 2 ;вот сколько прошивок ни посмотрел, везде так. никто это не исправляет, значит это не ошибка, ;а фича такая - принципиальная неподдерживаемость длинных секторов BNE ERR12 ; ERR12: Длинные - выходим с ошибкой 12 ; WORK: Собственно п/п чтения WORK: MOVB BRETRY(R3), CRETRY(R3) ; Восстановим счетчик по BRETRY MOVB #6, SECRET(R3) ; На поиск сектора - 6 попыток CLR FREE(R3) ; Остаток сектора отсутствует CMP WCNT(R3), SECLEN(R3) ; Читать нужно больше одного сектора? BHI 1$ ; Да - идем читать MOV SECLEN(R3), FREE(R3) SUB WCNT(R3), FREE(R3) ; Вычислим длину остатка 1$: MOVB #5, ERRNUM(R3) ; Прогнозируем ошибку 5 2$: MOVB #20., TURNS(R3) ; На операцию отводим 20. оборотов 3$: CALL FINDH ; FINDH: Ищем маркер заголовка BCC 4$ ; Нашли - идем дальше MOVB ERRNUM(R3), R0 ; Кончились обороты - выходим BR ERR ; с ошибкой ; ─────────────────────────────────────────────────────────────────────────── 4$: TSTB (R4) ; Готовность данных есть? BPL 4$ ; Нет - подождем CMP SIDE(R3), (R5) ; Нужная дорожка и сторона? BEQ 5$ ; Да - идем дальше DECB SECRET(R3) ; Минус одна попытка BEQ 6$ ; Кончились - выход с ошибкой CALL GOTO00 ; GOTO00: Позиционируем на дорожку 00, CALL GOTRK ; GOTRK: вернемся на нужную BR 3$ ; и попытаемся еще раз ; ─────────────────────────────────────────────────────────────────────────── 6$: MOV #4, R0 ; Выходим с ошибкой 4 BR ERR ; ERR ; ─────────────────────────────────────────────────────────────────────────── 5$: TSTB (R4) ; Ждем готовности данных BPL 5$ ; Не готовы - повторять... MOV (R5), R1 ; Получим номер сектора SWAB R1 ; поместим его в младший байт CMPB SECTOR(R3), R1 ; Этот сектор ищем? BEQ 7$ ; Да - будем его читать MOV SECLEN(R3), R0 ; Нет - возьмем длину данных, ADD #27, R0 ; прибавим длину заголовка CALL FICT ; FICT: и пропустим их BR 3$ ; Повторим процедуру еще раз ; ─────────────────────────────────────────────────────────────────────────── 7$: CALL TSTCRC ; TSTCRC: Проверим CRC BNE 8$ ; Совпала - идем дальше MOVB #2, ERRNUM(R3) ; Если что, это будет ошибка 2 DECB CRETRY(R3) ; Одной попыткой меньше BNE 2$ ; Еще остались - поищем снова MOVB ERRNUM(R3), R0 ; Нет - выходим с ошибкой BR ERROR ; ─────────────────────────────────────────────────────────────────────────── 8$: MOV SECLEN(R3), R1 ; Получим длину сектора SUB FREE(R3), R1 ; Вычислим длину используемой части ; Чтение сектора BITB #20, FLAGS(R3) ; Будем записывать? BEQ 9$ ; Нет - значит, читать JMP WRSEC ; Да - идем на запись сектора ; ─────────────────────────────────────────────────────────────────────────── 9$: CALL FINDS ; FINDS: Ищем зону синхронизации TSTB SECRET(R3) ; Нашли? BEQ 10$ ; Нет - пробуем снова CALL STREAD ; STREAD: Начинаем чтение! MOV #600., R0 12$: TSTB (R4) ; Данные готовы? BMI 11$ ; Да - посмотрим их SOB R0, 12$ ; Больше ждать нельзя? 10$: MOVB #11, ERRNUM(R3) ; Да - прогнозируем ошибку 11 DECB CRETRY(R3) ; Одной попыткой меньше BNE 2$ ; Попытки есть - повторим операцию MOVB ERRNUM(R3), R0 ; Кончились - выйдем с ошибкой BR ERROR ; ─────────────────────────────────────────────────────────────────────────── 11$: TST (R5) ; Пропустим маркер A1A1 13$: TSTB (R4) ; Готовность данных есть? BPL 13$ ; Нет - ждем MOV (R5), R0 ; Прочитаем данные CMP #120773, R0 ; Обычный сектор? BEQ 14$ ; Да - идем дальше CMP #120770, R0 ; Скрытый сектор? BNE 10$ ; Что-то иное - ошибка BITB #1, FLAGS(R3) ; Можно читать скрытые данные? BNE 14$ ; Да - будем читать MOV #13, R0 ; Нельзя - выходим с ошибкой 13 BR ERROR ; ─────────────────────────────────────────────────────────────────────────── 14$: CALL RDSEC ; RDSEC: Прочитаем сектор CALL TSTCRC ; TSTCRC: Проверим CRC BNE NEXT ; Совпала - посмотрим следующий сектор MOVB #1, ERRNUM(R3) ; Не совпала - ошибка 1 DECB CRETRY(R3) ; Попытки кончились? BNE 2$ ; Нет - попробуем еще раз MOVB ERRNUM(R3), R0 ; Кончились - выходим с ошибкой BR ERROR ; ─────────────────────────────────────────────────────────────────────────── ; Мультисекторные операции NEXT: SUB SECLEN(R3), WCNT(R3) ; Сектор обработали, вычтем из длины BLE EXIT ; Массив кончился - выход INCB SECTOR(R3) ; Нет - следующий сектор CMPB SECTOR(R3), MAXSEC(R3) ; Конец дорожки? BLOS 1$ ; Нет - продолжим обработку BIT #40, (R3) ; Верхняя сторона? BNE 2$ ; Да - следующая дорожка BITB #2, @FLGPTR(R3) ; Диск односторонний? BNE 2$ ; Да - продолжим обработку MOVB #1, SIDE(R3) ; Нет - установим верхнюю сторону BIS #40, (R3) BR 3$ ; Перейдем к обработке ; ─────────────────────────────────────────────────────────────────────────── 2$: INCB TRK(R3) ; Следующая дорожка CALL GOTRK ; Позиционируем на нее CLRB SIDE(R3) ; Установим нижнюю сторону BIC #40, (R3) 3$: MOVB #1, SECTOR(R3) ; Теперь сектор будет первым CALL CORR ; Переключить предкоррекцию 1$: ADD SECLEN(R3), ADDR(R3) ; Получим новый начальный адрес ADD SECLEN(R3), ADDR(R3) JMP WORK ; WORK: Идем на чтение очередного сектора ; ─────────────────────────────────────────────────────────────────────────── ; Обработка прерывания по вектору 4 VECT4: TST (R5) ; Сбросить данные MOV #7, R0 ; Заносим код ошибки 7 ; ERROR: Выход с ошибкой ERROR: MOVB R0, @#ERRFDD ; Код ошибки - на место! MOVB #1, SECRET(R3) ; Установим бит C в буфере BR EXIT1 ; EXIT1: Идем на выход ; ─────────────────────────────────────────────────────────────────────────── ; EXIT: Выход без ошибки EXIT: CLRB SECRET(R3) ; Сбросим бит C в буфере EXIT1: MOV BUF4(R3), @#4 ; Восстановить вектор 4 MTPS BUFPSW(R3) ; Восстановить PSW CCC ; Сбросить все признаки RORB SECRET(R3) ; Бит C - из буфера в PSW MOV BUFSP(R3), SP ; Восстановить SP RETURN ; Возврат ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; TSTCRC: П/п проверки CRC TSTCRC: TSTB (R4) ; Ждем готовности данных BPL TSTCRC MOV #15., R2 ; CRC появится не позже 15. циклов 1$: BIT #40000, (R4) ; CRC сошлась? BNE 2$ ; Да - выход SOB R2, 1$ ; Нет - очередной цикл 2$: RETURN ; Если бит Z=1, то произошла ошибка ; End of function TSTCRC ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; RDSEC: П/п чтения сектора в буфер; R1 - число слов для пересылки RDSEC: MTPS #340 ; Маскируем прерывания MOV ADDR(R3), R2 ; Получим адрес буфера 1$: TSTB (R4) ; Ожидаем данные BPL 1$ MOV (R5), R0 ; Читаем два байта, SWAB R0 ; делаем из них нормальное слово, MOV R0, (R2)+ ; которое помещаем в буфер SOB R1, 1$ ; Читаем заказанное число слов MOV FREE(R3), R1 ; Получаем длину остатка BEQ 2$ ; Остатка нет - сразу выходим 3$: TSTB (R4) ; Ждем данные BPL 3$ TST (R5) ; Вхолостую читаем остаток SOB R1, 3$ ; требуемой длины MTPS BUFPSW(R3) ; Восстанавливаем PSW 2$: RETURN ; End of function RDSEC ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; FINDS: П/п поиска синхропоследовательности FINDS: TST (R5) ; Сбрасываем прочитанное значение MOVB #100., SECRET(R3) ; Будем искать 100. циклов 1$: TSTB (R4) ; Ждем готовности BPL 1$ MOV (R5), R0 ; Получим прочитанные данные BEQ 2$ ; Это 0 - на выход INC R0 ; Это 177777? BEQ 2$ ; Да - на выход DECB SECRET(R3) ; Циклы кончились? BNE 1$ ; Нет - повторяем поиск 2$: RETURN ; End of function FINDS ; ─────────────────────────────────────────────────────────────────────────── ; П/п чтения двух слов (не используется) 11$: TSTB (R4) ; Ждем готовности BPL 11$ MOV (R5), R0 ; Получаем первое слово 12$: TSTB (R4) ; Ждем готовности BPL 12$ MOV (R5), R1 ; Получаем второе слово RETURN ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; STREAD: П/п запуска чтения STREAD: BIS #400, (R3) ; Установим признак "начало чтения" MOV (R3), (R4) ; и запишем в регистр BIC #400, (R3) ; Снимем признак MOV #10, R0 SOB R0, . ; Небольшая задержка... MOV (R3), (R4) ; и снова запишем в регистр RETURN ; End of function STREAD ; ─────────────────────────────────────────────────────────────────────────── ; П/п поиска начала индекса (не используется) 11$: TST (R4) ; Индекс есть? BMI 11$ ; Да - ждем исчезновения BIS #400, (R3) ; Установим признак "начало чтения" MOV (R3), (R4) ; и запишем в регистр 12$: TST (R4) ; Индекса еще нет? BPL 12$ ; Да - ждать появления MOV HOLTIN(R3), R0 SOB R0, . ; Задержимся... BIC #400, (R3) ; Сбросим признак "начало чтения" MOV (R3), (R4) ; и запишем в регистр RETURN ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; FINDH: П/п поиска адресного маркера FINDH: MOV #15., R0 1$: TST (R5) ; Вхолостую прочитаем SOB R0, 1$ ; 15. слов CLR INTIME(R3) ; Начинаем ожидание индекса BICB #40, FLAGS(R3) ; Пока индекса нет FH2$: BITB #30, @FLGPTR(R3) ; Сектор задан задержкой? BEQ 3$ ; Нет - обрабатывать CMPB SECTOR(R3), #1 ; Да - нужен первый сектор? BNE 3$ ; Второй и далее задаются заголовками MOV #1474, HOLTIN(R3) ;чудо-код, независимо от того, BR 4$ ;как задан сектор и какой нужен сектор ; ─────────────────────────────────────────────────────────────────────────── 3$: MOV #1474, HOLTIN(R3) ;выставляем вот такую задержку 4$: BR 5$ ; ─────────────────────────────────────────────────────────────────────────── ; продолжение п/п загрузки и запуска загрузчика BOOTC$: MOV #2000, R3 ; Установим адрес рабочей области CALL INIT ; INIT: и инициализируем ее MOVB R0, UNIT(R3) ; Занесем номер привода JMP BOOTN ; пойдем продолжать обычную загрузку на своем привычном месте ; ─────────────────────────────────────────────────────────────────────────── 5$: CALL INDEX ; INDEX: Проверим наличие диска и обороты BCS 6$ ; Обороты кончились - выходим BEQ 5$ ; Какие данные? Нули пропустим COM R0 ; 177777? BEQ 5$ ; Да - тоже пропустим 7$: CALL INDEX ; INDEX: Проверим наличие диска и обороты BCS 6$ ; Обороты кончились - выходим BEQ 8$ ; Какие данные? Нули - синхро COM R0 ; 177777? BNE 7$ ; Нет - пропустим ; Проверка адресного маркера 8$: MOV #3, R1 9$: MOV (R5), R0 ; Читаем данные BEQ 10$ ; Нули - продолжаем COM R0 ; 177777? BNE 5$ ; Нет - повторяем поиск 10$: SOB R1, 9$ ; Да - повторяем 3 раза CALL STREAD ; STREAD: Запускаем на чтение MOV HOLTIN(R3), R0 ; берем задержку 11$: TSTB (R4) ; Готовность есть? BMI 12$ ; Есть - считываем данные SOB R0, 11$ ; Нет - ждем BR FH2$ ; Надоело ждать - повторяем поиск ; ─────────────────────────────────────────────────────────────────────────── 12$: TST (R5) ; Пропускаем A1A1 13$: TSTB (R4) ; Ждем готовности BPL 13$ MOV (R5), R0 ; Есть - берем данные CMP #120776, R0 ; Адресный маркер (A1FE)? BEQ 14$ ; Да - нормальный выход CMP #120773, R0 ; Нет - маркер данных (A1FB)? BEQ 15$ ; Да - пропускаем сектор CMP #120770, R0 ; Нет - маркер скрытых данных (A1F8)? BNE FH2$ ; Нет - повторяем поиск сначала 15$: CALL FRESEC ; FRESEC: Да - пропускаем сектор BR FH2$ ; Повторяем поиск сначала ; ─────────────────────────────────────────────────────────────────────────── 14$: CLC 6$: RETURN ; End of function FINDH ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; INDEX: П/п проверки диска и числа оборотов INDEX: TST (R4) ; Индекс есть? BPL 1$ ; Нет - идем проверять BITB #40, FLAGS(R3) ; Индекс есть - он уже был? BNE 2$ ; Был - идем увеличивать время CLR INTIME(R3) ; Только что появился - очистим время BISB #40, FLAGS(R3) ; и запомним факт появления DECB TURNS(R3) ; Закончился последний оборот? BNE 3$ ; Нет - нормальный выход SEC ; Да - выход с ошибкой RETURN ; ─────────────────────────────────────────────────────────────────────────── 3$: CLC MOV (R5), R0 ; Забираем данные RETURN ; ─────────────────────────────────────────────────────────────────────────── 1$: BITB #40, FLAGS(R3) ; Индекса нет - а он был? BEQ 2$ ; Нет и не было - идем считать время CLR INTIME(R3) ; Только что исчез - очистим время BICB #40, FLAGS(R3) ; и запомним факт исчезновения BR 3$ ; Нормально выходим ; ─────────────────────────────────────────────────────────────────────────── 2$: INC INTIME(R3) ; Еще один такт индекс не менялся CMP INTIME(R3), #60000 ; Ну сколько можно! Пора кончать? BLOS 3$ ; Не пора - нормальный выход MOV #6, R0 ; Терпение кончилось - JMP ERROR ; ERROR: значит диска нет - ошибка 6 ; End of function INDEX ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; ENGINE: П/п выбора устройства ENGINE: MOV #177130, R4 ; Заполняем рабочие регистры MOV R4, R5 ; адресами PC TST (R5)+ ; РД КНГМД BICB #4, FLAGS(R3) ; Очистим признак работы двигателя BIT #20, (R3) ; Двигатель уже включен? BEQ 1$ ; Нет - включим BISB #4, FLAGS(R3) ; Да - установим признак 1$: BIS #20, (R3) ; Включим двигатель MOV (R3), (R4) ; и запишем в регистр BIC #57, (R3) ; Сбросим все другие регистры TSTB SIDE(R3) ; Сторона верхняя? BEQ 2$ ; Да - идем дальше BIS #40, (R3) ; Нет - включим нижнюю 2$: BICB #177774, UNIT(R3) ; Сбросим лишние биты MOVB UNIT(R3), R1 ; Получим номер привода MOV PC, R0 ; Получим базовый адрес ADD #DMASK-., R0 ; таблицы масок для приводов ADD R1, R0 ; Получим адрес нужной маски BISB (R0), (R3) ; Установим бит нужного привода ADD R3, R1 ; Получим базовый адрес ADD #TRKTAB, R1 ; таблицы TRKTAB MOV R1, CURTRK(R3) ; Настроим указатель CURTRK CALL SETPTR ; SETPTR: Настроим указатель FLGPTR BITB #4, FLAGS(R3) ; Мотор включен только что? BNE 3$ ; Нет - выходим CLR R1 ; Да - подождем раскручивания SOB R1, . SOB R1, . 3$: MOV (R3), (R4) ; Записываем установки в регистр MOV TDOWN(R3), R1 ; Подождем опускания головки SOB R1, . RETURN ; End of function ENGINE ; ─────────────────────────────────────────────────────────────────────────── ; Таблица масок для включения приводов DMASK: .BYTE 1,2,4,10 ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; SETPTR: П/п настройки указателя FLGPTR SETPTR: CLR FLGPTR(R3) ; Очистим указатель FLGPTR BICB #177740, UNIT(R3) ; Отбросим лишние биты (вот это и есть поддержка винта? в других-то местах по-прежнему все сбрасывается) MOVB UNIT(R3), FLGPTR(R3) ; Перешлем номер привода в FLGPTR CALL SETPT1 ; продолжим обработку и там всё равно отбросим ненужные биты, в FLGTAB ведь только 4 байта ADD #FLGTAB, FLGPTR(R3) ; получаем искомый адрес RETURN ; End of function SETPTR ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; GOTRK: П/п позиционирования GOTRK: TSTB TRK(R3) ; Нужна дорожка 00? BEQ GOTO00 ; Да - позиционируем прямо на нее TSTB @CURTRK(R3) ; Положение головки известно? BPL 1$ ; Да - будем искать требуемую CALL GOTO00 ; Нет - придется сориентироваться 1$: CMPB @CURTRK(R3), TRK(R3) ; Нужная дорожка совпадает с текущей? BEQ 2$ ; Да - так что же мы тут делаем? BHI 3$ ; Нет - искомая ближе к центру? CALL GOUP ; Да - шагаем к центру BR 1$ ; Проверим, дошли ли ; ─────────────────────────────────────────────────────────────────────────── 3$: CALL GODOWN ; Нет - шагнем к периферии BR 1$ ; Проверим, дошли ли ; ─────────────────────────────────────────────────────────────────────────── 2$: MOV #10000., R0 ; Подождем успокоения головки SOB R0, . RETURN ; Теперь можно и выйти ; End of function GOTRK ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; GOUP: Шаг к центру GOUP: INCB @CURTRK(R3) ; Номер дорожки станет на 1 больше BMI 1$ ; Таких дорожек нет - выходим с ошибкой BIS #100, (R3) ; Все в порядке - шагать будем вперед BR 3$ ; Пойдем шагать ; ─────────────────────────────────────────────────────────────────────────── 1$: MOV #4, R0 ; Выход с ошибкой 4 JMP ERROR ; End of function GOUP ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; GODOWN: Шаг к периферии GODOWN: DECB @CURTRK(R3) ; Номер дорожки станет на 1 меньше BIT #1, (R4) ; Дошли до дорожки 00? BEQ 2$ ; Нет - можно шагать CLRB @CURTRK(R3) ; Дошли: теперь текущая - 00 RETURN ; и можно выйти ; ─────────────────────────────────────────────────────────────────────────── 2$: BIC #100, (R3) ; Шагать будем назад 3$: MOV #200., R0 ; Подготовим задержку MOV (R3), (R4) ; Перешлем направление шага в регистр SOB R0, . ; Подождем, пока контроллер "переварит" BIS #200, (R3) ; Установим признак шага MOV (R3), (R4) ; и запишем в регистр MOV TSTEP(R3), R0 ; Получим задержку перехода дорожки SOB R0, . ; и задержимся BITB #1, @FLGPTR(R3) ; Двойной шаг? BEQ 4$ ; Нет - можно выходить BITB #100, FLAGS(R3) ; Мы возвращаемся на 00? BNE 4$ ; Да - возвращаться надо одинарным шагом MOV (R3), (R4) ; Нет - придется шагнуть еще MOV TSTEP(R3), R0 ; Получим задержку перехода дорожки SOB R0, . ; и задержимся 4$: BIC #200, (R3) ; Очистим признак шага RETURN ; End of function GODOWN ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; GOTO00: П/п возврата на дорожку 00 GOTO00: MOVB #200, @CURTRK(R3) ; Установим счетчик дорожек BISB #100, FLAGS(R3) ; Возвращаться надо одинарным шагом 1$: CALL GODOWN ; GODOWN: Шагнем к периферии BIT #1, (R4) ; Дошли до 00? BNE 2$ ; Да - выходим TSTB @CURTRK(R3) ; На какой дорожке должны находиться? BNE 1$ ; Не на нулевой - еще шагнем MOV #3, R0 ; На нулевой, а ее нет - ошибка 3 BICB #100, FLAGS(R3) ; Сбросим режим одинарного шага JMP ERROR ; Выходим с ошибкой ; ─────────────────────────────────────────────────────────────────────────── 2$: CLRB @CURTRK(R3) ; Дошли - текущая теперь нулевая BICB #100, FLAGS(R3) ; Сбросим режим одинарного шага RETURN ; End of function GOTO00 ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; FRESEC: П/п пропуска сектора FRESEC: MOV SECLEN(R3), R0 ; Получим длину сектора ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ FICT: TST (R4) ; Индекс есть? BMI 1$ ; Да - ждем начала оборота 2$: TST (R4) ; Индекс все еще есть? BMI 3$ ; Да - оборот завершен 4$: TSTB (R4) ; Данные готовы? BPL 2$ ; Нет - снова TST (R5) ; Прочитаем данные вхолостую SOB R0, 2$ ; требуемое число раз BR 5$ ; и выйдем ; ─────────────────────────────────────────────────────────────────────────── 3$: DECB TURNS(R3) ; Все обороты вышли? BNE 6$ ; Нет - попытаемся слова MOV #5, R0 ; Да - выходим с ошибкой 5 JMP ERROR ; ─────────────────────────────────────────────────────────────────────────── 1$: TST (R4) ; Индекс исчез? BPL 4$ ; Да - оборот начался 6$: TSTB (R4) ; Данные готовы? BPL 1$ ; Нет - проверим индекс TST (R5) ; Прочитаем данные вхолостую SOB R0, 1$ ; требуемое число раз 5$: RETURN ; и вернемся к основной работе ; End of function FICT ; End of function FRESEC ; ─────────────────────────────────────────────────────────────────────────── ; WRSEC: П/п записи сектора ; Входные параметры: R1 - число слов для записи WRSEC: MOV ADDR(R3), R2 ; Получим начальный адрес массива MOV #11., R0 ; В маркере будет 11. 4E4E MTPS #340 ; При записи нам не мешать! CALL WRMAR ; Запишем маркер данных 1$: TSTB (R4) ; Готовность есть? BPL 1$ ; Нет - ждем MOV (R2)+, (R5) ; Запишем первое слово данных MOV (R3), (R4) ; Снимем признак записи маркера BR 2$ ; и пойдем записывать данные ; ─────────────────────────────────────────────────────────────────────────── 3$: TSTB (R4) ; Готовность есть? BPL 3$ ; Нет - дождемся MOV (R2)+, (R5) ; Запишем слово данных 2$: SOB R1, 3$ ; и так до конца MOV FREE(R3), R1 ; Получим длину пустого остатка сектора BEQ 4$ ; Остатка нет - выходим сразу 5$: TSTB (R4) ; Готовность есть? BPL 5$ ; Дожидаемся MOV #0, (R5) ; Записываем нули SOB R1, 5$ ; в нужном количестве 4$: BIT #40000, (R4) ; Ждем записи CRC BEQ 4$ MOV #47116, (R5) ; Записываем 4E4E 6$: TSTB (R4) ; Готовность есть? BPL 6$ ; Нет - ждем TST (R5) ; Прекращаем запись MTPS BUFPSW(R3) ; Восстанавливаем PSW JMP NEXT ; NEXT: Продолжаем запись массива ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; WRMAR: П/п записи маркера данных ; Входные данные: R0 - число записываемых слов 4E4E WRMAR: MOV #47116, (R5) ; Запишем 4E4E BR 1$ ; Идем на повтор записи ; ─────────────────────────────────────────────────────────────────────────── 2$: TSTB (R4) ; Готовность есть? BPL 2$ ; Нет - дождемся MOV #47116, (R5) ; Запишем 4E4E 1$: SOB R0, 2$ ; Повторим заданное число раз MOV #6, R0 ; Запишем 12. нулевых байтов 3$: TSTB (R4) ; Готовность есть? BPL 3$ ; Нет - ожидаем MOV #0, (R5) ; Записываем нули SOB R0, 3$ ; в нужном количестве BIS #1000, (R3) ; Подготовим признак записи маркера 4$: TSTB (R4) ; Контроллер готов? BPL 4$ ; Нет - ждем MOV #120641, (R5) ; Записываем маркер A1A1 MOV (R3), (R4) ; Передаем признак в контроллер BIC #1000, (R3) ; Заранее снимем признак 5$: TSTB (R4) ; Готовность есть? BPL 5$ ; Нет - ожидаем MOV MARKER(R3), (R5) ; Записываем маркер из буфера RETURN ; End of function WRMAR ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; CORR: П/п установки прекоррекции CORR: BIC #2000, (R3) ; Снимем прекоррекцию CMPB TRKCOR(R3), @CURTRK(R3) ; Надо включать на текущей дорожке? BHI 1$ ; Нет - пропустим BIS #2000, (R3) ; Пора - включим 1$: MOV (R3), (R4) ; Перешлем управляющее слово MOV #200., R0 SOB R0, . ; Подождем, пока контроллер воспримет RETURN ; End of function CORR ; ─────────────────────────────────────────────────────────────────────────── ; FORMAT: П/п форматирования FORMAT: MOV SP, BUFSP(R3) ; Сохраним SP, MFPS BUFPSW(R3) ; PSW, MOV #4, R0 MOV (R0), BUF4(R3) ; @#4 в соответствующих буферах MOV PC, (R0) ; Переназначаем вектор @#4 на себя, ADD #VECT4-., (R0) ; теперь он равен 161512 CALL ENGINE ; Запустим двигатель CALL GOTRK ; Позиционируем на нужную дорожку CALL CORR ; Установим прекоррекцию BIT #4, (R4) ; Защита записи включена? BEQ 1$ ; Нет - начинаем форматировать MOV #1, R0 ; Да - ошибка 1 2$: JMP ERROR ; Выходим с ошибкой ; ─────────────────────────────────────────────────────────────────────────── 1$: SWAB SIDE(R3) ; Обмениваем номера стороны и дорожки MOVB FILLB(R3), WRTVAR(R3) ; Делаем слово-заполнитель MOVB FILLB(R3), WRTVAR+1(R3) ; из байта-заполнителя MOVB MAXSEC(R3), R1 ; Получим длину сектора MOV #1001, R2 ; Запомним номер сектора и код длины MOV #1000., R0 ; Время ожидания индекса 3$: TST (R4) ; Индекс есть? BPL 4$ ; Исчез - начинаем записывать SOB R0, 3$ ; Есть - ждем исчезновения 5$: TST (R5) ; Так и не исчез - прекратим запись MOV #6, R0 ; Диск не вращается - ошибка 6 SWAB SIDE(R3) ; Вернем все на место BR 2$ ; Выходим с ошибкой ; ─────────────────────────────────────────────────────────────────────────── ; Запись на дорожку кода 4E4E 4$: MOV #6200, R0 ; Установим максимальную длину MTPS #340 ; При записи нам не мешать MOV #47116, (R5) ; Записываем 4E4E 6$: TST (R4) ; Индекс появился? BMI 7$ ; Да - начинаем запись 8$: TSTB (R4) ; Готовность есть? BPL 8$ ; Нет - ждем MOV #47116, (R5) ; Записываем 4E4E SOB R0, 6$ ; И так до конца дорожки BR 5$ ; Дорожка длинная, ошибка! ; ─────────────────────────────────────────────────────────────────────────── 7$: MOV #16., R0 ; В маркере будет 16. 4E4E (GAP1) MOV #177241, MARKER(R3) ; Адресный маркер (FEA1) - в буфер 9$: CALL WRMAR ; WRMAR: Записываем адресный маркер 10$: TSTB (R4) ; Готовность есть? BPL 10$ ; Нет - ожидаем MOV SIDE(R3), (R5) ; Записываем номер стороны и дорожки MOV (R3), (R4) ; Перешлем управляющее слово 11$: TSTB (R4) ; Готовность есть? BPL 11$ ; Нет - дождемся MOV R2, (R5) ; Записываем номер сектора и код длины MOV #11., R0 ; В маркере будет 11. 4E4E (GAP2) MOV #175641, MARKER(R3) ; Маркер данных (FBA1) - в буфер 12$: BIT #40000, (R4) ; Ждем записи CRC BEQ 12$ CALL WRMAR ; WRMAR: Записываем маркер данных MOV SECLEN(R3), R0 ; Получаем длину сектора DEC R0 ; Одно слово запишем сейчас 13$: TSTB (R4) ; Готовность есть? BPL 13$ ; Нет - ожидаем MOV WRTVAR(R3), (R5) ; Записываем заполнитель MOV (R3), (R4) ; Перешлем управляющее слово 14$: TSTB (R4) ; Готовность есть? BPL 14$ ; Нет - дождемся MOV WRTVAR(R3), (R5) ; Записываем заполнитель SOB R0, 14$ ; И так до конца сектора INC R2 ; Увеличим номер сектора MOV #24., R0 ; В маркере будет 24. 4E4E (GAP3) ; Примечание. Если увеличить GAP3 до 24-27 слов, то запись будет производиться так ; же быстро, как и чтение (обычно заголовок очередного сектора проскакивает, и дорожка ; записывается не за один, а за 10 оборотов). Однако при превышении этой величиной ; определенного значения конец последнего сектора дорожки может наложиться на ее ; же начало. BIT #4, @FLGPTR(R3) ; Сектора длинные? BEQ 15$ ; Нет - все нормально MOV #72, R0 ; Да - будет 116. 4E4E (можно увеличить до 75) 15$: MOV #177241, MARKER(R3) ; Адресный маркер (FEA1) - в буфер 16$: BIT #40000, (R4) ; Ждем записи CRC BEQ 16$ SOB R1, 9$ ; И так все сектора до конца дорожки ; Окончание записи дорожки 17$: TSTB (R4) ; Готовность есть? BPL 17$ ; Нет - ждем MOV #47116, (R5) ; Запишем 4E4E TST (R4) ; Индекс есть? BPL 17$ ; Нет - продолжим запись до появления 18$: TSTB (R4) ; Готовность есть? BPL 18$ ; Нет - дождемся MOV #47116, (R5) ; Запишем 4E4E TST (R5) ; Прекратим запись MTPS BUFPSW(R3) ; Восстановим PSW SWAB SIDE(R3) ; Вернем все на место JMP EXIT ; EXIT: Выходим ; ─────────────────────────────────────────────────────────────────────────── ; Драйвер расширенной арифметики ; ─────────────────────────────────────────────────────────────────────────── ; эмуляция прерывания по вектору 4 EXT$V4: MOV @#6, -(SP) ; в стек PS вектора 4 BIS #1, (SP) ; установим там бит С MOV @#4, -(SP) ; в стек адрес вектора 4 RTI ; и переходим. ; в стеке остались PS/PC выхода из вектора 10. ; они будут использоваться при выходе из вектора 4 ; или, если там стек напрямую инициализируется, то они не нужны ; ─────────────────────────────────────────────────────────────────────────── ;точка входа в драйвер расширенной арифметики. ;вход - прерывание по вектору 10 EXT: MOV #6, -(SP) ADD SP, (SP) ; тут получается значение стека до возникновения прерывания ; это нужно, если для передачи параметров fis использовался SP MOV R5, -(SP) MOV R4, -(SP) MOV 6(SP), R5 ; берём адрес возврата из прерывания MOV -(R5), R4 ; получаем опкод, вызвавший прерывание MOV R3, -(SP) MOV R2, -(SP) MOV R1, -(SP) MOV R0, -(SP) MOV R4, R0 ; опкод MOV R0, R1 ; ещё раз CMP R0, #75000 ; это fis ? BHIS FLOAT$ ; скорее всего да, пойдём проверим JMP EXTAR$ ; нет, может eis? пойдём проверим ; ─────────────────────────────────────────────────────────────────────────── ADD #6, SP ; мусор ERRCM$: CALL RESTR$ ; восстановим регистры JMP EXT$V4 ; и сделаем якобы прерывание по вектору 4 ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ;п/п установки признаков и восстановления регистров RSTRS$: BIC #17, 22(SP) ; чистим поле признаков в PS возврата из прерывания BIS R5, 22(SP) ; и устанавливаем там свои ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ;п/п восстановления регистров RESTR$: MOV (SP)+, 14(SP) ; берём адрес возврата из RESTR$ или RSTRS$, и сохраняем его ; на месте значения 6+SP MOV (SP)+, R0 ; восстанавливаем все регистры MOV (SP)+, R1 MOV (SP)+, R2 MOV (SP)+, R3 MOV (SP)+, R4 MOV (SP)+, R5 RETURN ; и выходим из п/п. Теперь в стеке только PC PS ; для выхода из прерывания ; End of function RESTR$ ; End of function RSTRS$ ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ;умножение R0:R1 = R3:R4 * R5 IMUL32: CLR R1 ; тут накапливается результат CLR R0 ROR R5 ; B.hi >> 1 BR 1$ 2$: ASR R5 1$: BCS 3$ ; если 1, то пойдём делать сложение BNE 4$ ; если 0, но ещё есть биты, то пойдём просто сдвигать BR 5$ ; если всё - то выход. 3$: ADD R4, R1 ; R0:R1 += R3:R4 ADC R0 ADD R3, R0 4$: ASL R4 ; R3:R4 <<= 1 ROL R3 BR 2$ ; и повторим 5$: RETURN ; как-то не оптимально. можно 2 слова сэкономить ; End of function IMUL32 ; ─────────────────────────────────────────────────────────────────────────── FLOAT$: CMP R0, #75040 ; точно fis ? BLO 1$ ; да JMP ERRCM$ ; нет - другие не эмулируемые опкоды ; ─────────────────────────────────────────────────────────────────────────── 1$: BIC #177747, R0 ; определим тип опкода ASR R0 ASR R0 ; номер опкода * 2 BIC #177770, R1 ; определим номер используемого регистра ASL R1 ; регистр * 2 ADD SP, R1 ; это адрес в стеке, где сохранено значение регистра MOV R1, -(SP) ; и сохраним его в стеке. Туда результат ещё писать надо будет MOV (R1), R1 ; достанем значение регистра MOV (R1)+, R2 ; B.hi MOV (R1)+, R3 ; B.lo MOV (R1)+, R4 ; A.hi MOV (R1)+, R5 ; A.lo MOV R1, -(SP) ; и снова сохраним ADD PC, R0 ADD #OFSTBL-., R0 ADD (R0), PC ; и пойдём обрабатывать конкретную операцию OFSTBL: .WORD FADD$-OFSTBL ; 75000 .WORD FSUB$-OFSTBL ; 75010 .WORD FMUL$-OFSTBL ; 75020 .WORD FDIV$-OFSTBL ; 75030 ; ─────────────────────────────────────────────────────────────────────────── ;деление A = A / B FDIV$: CLR -(SP) ; флаг знака результата MOV R2, R0 ; сохраним порядок B BGT 1$ ; если больше 0 - переход туда BLT 2$ ; если меньше 0 - переход туда JMP FERR$ ; если равно 0 - ошибка, на 0 делить нельзя ; ─────────────────────────────────────────────────────────────────────────── 2$: INC (SP) ; отмечаем смену знака результата 1$: MOV R4, R1 ; сохраним порядок A BEQ F3$ ; если это 0, то результат деления == 0 BPL 4$ ; если больше 0 - переход INC (SP) ; отмечаем смену знака результата 4$: CALL SEPAR ; разделяем мантиссу и порядок SUB R0, R1 ; res.exp = A.exp - B.exp ADD #37600, R1 ; ? MOV R1, -(SP) ; сохраняем MOV #100, R1 ; начальное значение, бит, по которому будет выход из цикла CLR R0 BR 5$ ; и идём просто делить R4:R5 на R2:R3 ; ─────────────────────────────────────────────────────────────────────────── 6$: ASL R5 ROL R4 5$: SUB R3, R5 SBC R4 SUB R2, R4 BMI 7$ 8$: SEC ROL R1 ROL R0 BCC 6$ BR 9$ ; ─────────────────────────────────────────────────────────────────────────── 10$: ASL R5 ROL R4 ADD R3, R5 ADC R4 ADD R2, R4 BPL 8$ 7$: CLC ROL R1 ROL R0 BCC 10$ 9$: MOV R0, R2 ; вот результат MOV R1, R3 ; тут R1 == R3 JMP NRRT$ ; и по идее, R1 не нужен, при сдвиге вправо ; ─────────────────────────────────────────────────────────────────────────── F3$: TST (SP)+ CLR R2 CLR R3 JMP FL$NZ$ ; установка признаков N и Z ; ─────────────────────────────────────────────────────────────────────────── ;умножение A = A * B FMUL$: CLR -(SP) ; флаг знака MOV R2, R0 ; сохраним порядок B BEQ F3$ ; если это 0, то результат умножения == 0 BPL 1$ ; если > 0, то переход INC (SP) ; иначе результат будет отрицательный 1$: MOV R4, R1 ; сохраним порядок A BEQ F3$ ; если это 0, то результат умножения == 0 BPL 2$ ; если > 0, то переход INC (SP) ; иначе результат будет отрицательный, или, если B было <0, то положительный 2$: CALL SEPAR ; разделяем мантиссу и порядок ADD R0, R1 ; res.exp = A.exp + B.exp SUB #40000, R1 ; ? MOV R1, -(SP) ; порядок результата MOV R5, -(SP) ; A.m.lo -> MOV R4, -(SP) ; A.m.hi -> MOV R3, -(SP) ; B.m.lo -> MOV R4, R3 ; A.m.hi MOV R5, R4 ; A.m.lo MOV R2, R5 ; B.m.hi CALL IMUL32 ; умножение R0:R1 = R3:R4 * R5 ; умножаем A.m на B.m.hi MOV R1, -(SP) ; результат сохраним MOV R0, -(SP) MOV 4(SP), R5 ; B.m.lo BEQ 3$ ; если 0, незачем умножать MOV 6(SP), R4 ; А.m.hi CLR R3 CALL IMUL32 ; умножение R0:R1 = R3:R4 * R5 ; умножаем A.m.hi на B.m.lo ADD R1, 2(SP) ; складываем с предыдущем результатом ADC (SP) ADD R0, (SP) MOV 10(SP), R4 ; A.m.lo BEQ 3$ ; если 0, незачем умножать MOV 4(SP), R5 ; B.m.lo CLR R3 CALL IMUL32 ; умножение R0:R1 = R3:R4 * R5 ; умножаем A.m.lo на B.m.lo ADD R0, 2(SP) ; складываем с предыдущем результатом ADC (SP) ; что-то не пойму логику. ; res = A.m * B.m.hi + (A.m.hi + A.m.lo) * B.m.lo 3$: CLR R2 BISB 1(SP), R2 ; берём старший байт ст слова результата MOV 2(SP), R1 ; берём мл слово результата SWAB R1 ; переставляем байты MOVB (SP)+, (SP) ; замещаем младший байт мл слова младшим байтом ст слова результата MOV (SP)+, R3 ; и достаём это SWAB R3 ; переставляем байты ; в результате получается res = R2:R3:R1 ; в старшем байте R2 - 0, в младшем байте R1 - незначащий мусор ADD #6, SP ; освобождаем стек JMP NRLT$ ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ SEPAR: BIC #100177, R0 ; выделяем порядок B (B.exp) BIC #100177, R1 ; выделяем порядок A (A.exp) BIC #177600, R2 ; выделяем мантиссу B (B.m) BIC #177600, R4 ; выделяем мантиссу A (A.m) BIS #200, R2 ; добавляем подразумеваемый бит в B.m BIS #200, R4 ; добавляем подразумеваемый бит в A.m RETURN ; End of function SEPAR ; ─────────────────────────────────────────────────────────────────────────── ;вычитание A = A - B - это A + (-B) FSUB$: TST R2 ; B == 0 ? ;тут таки не учитывается, что если порядок 0, то в мантиссе может быть ;любой мусор, он должен просто игнорироваться. BEQ FADD$ ;да, пойдём, прибавим 0 ADD #100000, R2 ;иначе, поменяем знак B = -B ;сложение A = A + B FADD$: MOV R2, R0 ; что мы там прибавляем? BNE 1$ ; не ноль MOV R4, R2 ; а если 0, то просто MOV R5, R3 ; res = A BR FL$NZ$ ; установка признаков N и Z ; ─────────────────────────────────────────────────────────────────────────── 1$: MOV R4, R1 ; а если A == 0, то res = B BEQ FL$NZ$ ; установка признаков N и Z BIC #177600, R2 ; оставляем в R2:R3 только мантиссу B (B.m) BIS #200, R2 ; добавляем подразумеваемый бит TST R0 ; а число B какое? BPL 2$ ; положительное NEG R3 ; отрицательное - поменяем знак мантиссы ADC R2 NEG R2 2$: BIC #177600, R4 ; оставляем в R4:R5 только мантиссу A (A.m) BIS #200, R4 ; добавляем подразумеваемый бит TST R1 ; а число A какое? BPL 3$ ; положительное NEG R5 ; отрицательное - поменяем знак мантиссы ADC R4 NEG R4 3$: BIC #100177, R0 ; это порядок B (B.exp) BIC #100177, R1 ; это порядок A (A.exp) CLR -(SP) ; флаг знака MOV R0, -(SP) ; B.exp SUB R1, R0 ; B.exp = B.exp - A.exp BHI 4$ ; если B.exp > A.exp BLO 5$ ; если B.exp < A.exp ;если B.exp == A.exp CLR R1 ; B.exp = A.exp = 0 BR 6$ ;B.exp < A.exp 5$: MOV R1, (SP) ; вместо B.exp поместим A.exp NEG R0 ; A.exp - B.exp MOV R2, R1 ; B.m <--> A.m MOV R4, R2 ; меняем местами мантиссы A и B MOV R1, R4 MOV R3, R1 MOV R5, R3 MOV R1, R5 ;B.exp > A.exp 4$: CLR R1 ; tmp = 0 (вытесненные биты из выровненной мантиссы) CMP R0, #6000 BLE 7$ ; B.exp - A.exp <= 24. CLR R4 ; если больше, то A.m = 0 CLR R5 BR 6$ ;B.exp - A.exp <= 24. 7$: ASL R0 SWAB R0 ; разность B.exp - A.exp в мл байт ;выравниваем мантиссы в соответствии с порядками. 8$: ASR R4 ; и двигаем A.m вправо ROR R5 ROR R1 ; а сюда принимаем биты SOB R0, 8$ 6$: ADD R5, R3 ; res.m = B.m + A.m ADC R2 ADD R4, R2 BPL 9$ ; если >= 0, переход NEG R1 ; иначе меняем знак ADC R3 ADC R2 NEG R3 ADC R2 NEG R2 INC 2(SP) ; и фиксируем этот факт 9$: MOV R3, R4 ; проверяем, что получилось BIS R1, R4 BIS R2, R4 BNE NRRT$ ; что-то получилось - переход CMP (SP)+, (SP)+ ; все нули, освобождаем стек BR FL$NZ$ ; установка признаков N и Z ; ─────────────────────────────────────────────────────────────────────────── ; сдвигаем мантиссу вправо NRRT$: CMP R2, #400 ; res.hi умещается в мл байт? BLO NRLT$ ; да ASR R2 ; нет, двигаем вправо ROR R3 ROR R1 ADD #200, (SP) ; считаем кол-во сдвигов (это добавка к порядку) BR NRRT$ ; и так пока не станет умещаться. ; ─────────────────────────────────────────────────────────────────────────── ; сдвигаем мантиссу влево NRLT$: TSTB R2 ; старший бит мантиссы == 1 ? BMI 1$ ; да 2$: SUB #200, (SP) ; нет - считаем кол-во сдвигов ASL R1 ; двигаем влево ROL R3 ROLB R2 BPL 2$ ; и так, пока старший бит мантиссы не станет 1 1$: ASL R1 ; округляем до двухсловного числа ADC R3 ADCB R2 BCC 3$ ; переполнения не случилось? ADD #200, (SP) ; случилось - скорректируем порядок. 3$: MOV (SP)+, R4 ; достаём порядок из стека BPL 4$ ; если >=0, то переход CLR R5 ; а иначе - переполнение ROL R4 ; двигаем порядок влево BPL F6$ ; если стало >=0, то V BR F5$ ; иначе N и V ;по идее, тут ещё и C должно формироваться, если С==1, но нет. ; ─────────────────────────────────────────────────────────────────────────── 4$: ROR (SP)+ ; достаём знак результата BCC 6$ ; если +, то переход BIS #100000, R2 ; если -, то формируем знаковый бит 6$: BIC #200, R2 ; очищаем подразумеваемый бит мантиссы BIS R4, R2 ; помещаем порядок на место ;установка признаков N и Z FL$NZ$: CLR R5 ; тут формируются признаки TST R2 ; res.hi == 0 ? BNE 1$ ; нет BIS #4, R5 ; да - формируем Z 1$: BPL 2$ ; res.hi >= 0 или просто переход после bis BIS #10, R5 ; res.hi < 0 - формируем N 2$: MOV (SP)+, R1 ; конец блока параметров MOV (SP)+, R0 ; адрес в стеке, где хранится значение используемого регистра MOV R3, -(R1) ; сохраняем res на место A MOV R2, -(R1) MOV R1, (R0) ; а адрес res - в регистр SUB SP, R0 ; а теперь проверим, какой это был регистр CMP R0, #14 BLO 3$ ; если это R0-R5, то всё как обычно BEQ 4$ ; если это SP ADD #4, 16(SP) ; если это PC - то он должен указывать за res, ; а не на него, как все остальные регистры 3$: CALL RSTRS$ ; установим признаки из R5 и восстановим регистры RTI ; ─────────────────────────────────────────────────────────────────────────── 4$: CALL RSTRS$ ; установим признаки из R5 и восстановим регистры MOV (SP)+, 2(SP) ; переместим PS/PC в стеке на место B, MOV (SP)+, 2(SP) ; оно всё равно не нужно RTI ; и выходим. Изящно получается со стеком в качестве буфера ; ─────────────────────────────────────────────────────────────────────────── FERR$: BIS #1, R5 ; формируем C F5$: BIS #10, R5 ; формируем N F6$: BIS #2, R5 ; формируем V CMP (SP)+, (SP)+ ; убираем из стека флаг знака и конец блока параметров fis MOV (SP)+, R0 ; достаём адрес в стеке, где хранится значение используемого регистра SUB SP, R0 ; проверим, какой это был регистр CMP R0, #16 ; это PC? BNE 1$ ; нет ADD #10, 16(SP) ; да - передвинем его за блок параметров 1$: CALL RSTRS$ ; установим признаки из R5 и восстановим регистры MOV @#246, -(SP) ; эмулируем возникновение прерывания по ошибке MOV @#244, -(SP) ; СППЗ RTI ; если кто об этом не знал, ему же хуже. ; ─────────────────────────────────────────────────────────────────────────── EXTAR$: CMP R0, #70000 ; точно eis? BHIS 1$ ; да JMP ERRCM$ ; нет - другие не эмулируемые опкоды ; ─────────────────────────────────────────────────────────────────────────── 1$: BIC #170777, R0 ; выделим код опкода SWAB R0 ; вот он уже умноженный на 2 MOV R1, R2 BIC #177770, R1 ; выделим регистр источника ROL R1 ; *2 ASR R2 ASR R2 MOV R2, R3 BIC #177761, R2 ; выделим код адресации источника *2 ASR R3 ASR R3 ASR R3 BIC #177761, R3 ; выделим регистр приёмника *2 MOV #2, R4 BIS R3, R4 ; а это R+1 приёмника ADD SP, R3 ; находим адрес в стеке, где находится содержимое регистров приёмника ADD SP, R4 MOV R1, -(SP) ; регистр источника сохраним ADD SP, R1 ; находим адрес в стеке, где находится содержимое регистра источника ADD #2, R1 MOV R2, -(SP) ; адресацию источника в стек ADD PC, (SP) ; и сформируем там адрес перехода ADD #EPCBS2-., (SP) ; а при этом у нас R5 свободен ADD @(SP)+, PC ; зачем надо было стек использовать, непонятно EPCBS2: .WORD ADDR01-EPCBS2 ; 0 Rx .WORD ADDR01-EPCBS2 ; 1 (Rx) .WORD ADDR23-EPCBS2 ; 2 (Rx)+ .WORD ADDR23-EPCBS2 ; 3 @(Rx)+ .WORD ADDR45-EPCBS2 ; 4 -(Rx) .WORD ADDR45-EPCBS2 ; 5 @-(Rx) .WORD ADDR67-EPCBS2 ; 6 n(Rx) .WORD ADDR67-EPCBS2 ; 7 @n(Rx) ; ─ автоинкрементная адресация ────────────────────────────────────────────── ADDR23: INC (SP) ; номер регистра источника ++ - признак автоинкрементной адресации MOV (R1), R5 ; берём содержимое регистра источника ADD #2, (R1) ; инкрементируем BR ARROP$ ; ─ автодекрементная адресация ────────────────────────────────────────────── ADDR45: SUB #2, (R1) ; декрементируем MOV (R1), R5 ; берём содержимое регистра источника BR ARROP$ ; ─ индексная адресация ───────────────────────────────────────────────────── ADDR67: MOV @20(SP), R5 ; берём индекс по содержимому PC ADD #2, 20(SP) ; прибавляем содержимому PC два ADD (R1), R5 ; прибавляем к индексу содержимое регистра источника BR ARROP$ ; ─ регистровая адресация ─────────────────────────────────────────────────── ADDR01: MOV R1, R5 ; берём регистр источник ARROP$: BIT #2, R2 ; адресация относительная? BEQ 1$ ; нет MOV (R5), R5 ; да - значит получим значение по адресу значения 1$: MOV R4, -(SP) ; R+1 приёмника MOV R3, -(SP) ; R приёмника ADD PC, R0 ADD #OFSTB2-., R0 ADD (R0), PC OFSTB2: .WORD MUL$-OFSTB2 ; 70000 .WORD DIV$-OFSTB2 ; 71000 .WORD ASH$-OFSTB2 ; 72000 .WORD ASHC$-OFSTB2 ; 73000 .WORD XOR$-OFSTB2 ; 74000 ; ─────────────────────────────────────────────────────────────────────────── ; умножение R:R+1 = R * ss MUL$: MOV (R3), R4 ; берём мл. часть BEQ 2$ ; если 0, то результат 0 MOV (R5), R5 ; берём источник BEQ 2$ ; если 0, то результат 0 BPL 1$ ; если >= 0, то идём умножать NEG R4 ; иначе R4 = -R4 NEG R5 ; R5 = -R5 BPL 1$ ; если >= 0, то идём умножать ; иначе в R5 число 100000 MOV R4, R3 ; R4 <-> R5 MOV R5, R4 MOV R3, R5 BPL 1$ ; если >= 0, то идём умножать NEG R5 ; иначе R5 = -R5 BMI 4$ ; если < 0, то и в R4 тоже было число 100000 1$: TST R4 ; умножаем SXT R3 CALL IMUL32 ; умножение R0:R1 = R3:R4 * R5 ; на выходе в R5 нуль TST R1 ; посмотрим SXT R2 ; какой знак у мл. части CMP R0, R2 ; если знак распространён на ст. часть BEQ 3$ ; то всё нормально BIS #1, R5 ; иначе бит C 3$: JMP EARX1$ 2$: CLR R0 ; результат CLR R1 CLR R5 ; флаги JMP EARX1$ ; выходим 4$: MOV #40000, R0 ;результат умножения 100000 * 100000 CLR R1 CLR R5 BR 3$ ; ─────────────────────────────────────────────────────────────────────────── ; деление R = R:R+1 / ss , R+1 = R:R+1 % ss DIV$: CLR -(SP) ; флаг знака результата MOV (R3), R0 ; значение R приёмника MOV (R4), R1 ; значение R+1 приёмника MOV R0, -(SP) BPL 1$ INC 2(SP) NEG R1 ADC R0 NEG R0 BMI 2$ 1$: MOV (R5), R4 BEQ 3$ BPL 4$ INC 2(SP) NEG R4 BMI 2$ 4$: CLR R5 CMP R0, R4 BGT 2$ BLT 5$ CMP R1, R4 BHIS 2$ 5$: CLR R2 MOV #20, R3 6$: ASR R4 ROR R5 SUB R5, R1 SBC R0 SUB R4, R0 BMI 7$ 8$: SEC ROL R2 SOB R3, 6$ BR 9$ 10$: ASR R4 ROR R5 ADD R5, R1 ADC R0 ADD R4, R0 BPL 8$ 7$: CLC ROL R2 SOB R3, 10$ ADD R5, R1 9$: TST (SP)+ BPL 11$ NEG R1 11$: ASR (SP)+ BCC 12$ NEG R2 12$: CLR R5 MOV R2, R0 BPL 13$ BIS #10, R5 ;N 13$: BNE 14$ BIS #4, R5 ;Z 14$: JMP EARX2$ 3$: MOV #3, R5 ; флаги VC BR 15$ 2$: MOV #2, R5 ; переполнение V 15$: ADD #10, SP ; результат не сохраняется JMP EARX3$ ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ;получаем R и число битов для сдвигаем ;выход: R2 = 0 ; R0 = R ; R5 = счётчик сдвигов ; Z - в счётчике 0 ; С - сдвиг вправо SRCASH: CLR R2 MOV (R3), R0 ; значение R приёмника MOV (R5), R5 ; значение источника BIT #40, R5 ; смотрим знак BEQ 1$ ; + BIS #177700, R5 ; - , устанавливаем биты NEG R5 ; и меняем знак RETURN 1$: BIC #177700, R5 ; очищаем биты RETURN ; End of function SRCASH ; ─────────────────────────────────────────────────────────────────────────── ;арифметический сдвиг R = R << ss / R = R >> ss ASH$: MOV R3, 2(SP) ; R+1 заменяем на R CALL SRCASH ; подготовка BEQ 1$ ; 0, не надо ничего двигать BCS 2$ ; двигать вправо 3$: ASL R0 ; двигаем влево BVC 4$ INC R2 ; а тут отмечаем возникновение бита V 4$: SOB R5, 3$ ; на заданное количество бит BR 1$ 2$: ASR R0 ; двигаем вправо, тут переполнения не бывает SOB R5, 2$ ; на заданное количество бит 1$: MOV R0, R1 ; результат дублируем и для R+1 BR EARX4$ ; ─────────────────────────────────────────────────────────────────────────── ;арифметический сдвиг R:R+1 = R:R+1 << ss / R:R+1 = R:R+1 >> ss ASHC$: MOV (R4), R1 ; значение R+1 приёмника CALL SRCASH ; подготовка BEQ EARX4$ ; 0, не надо ничего двигать BCS 1$ ; двигать вправо 2$: ASL R1 ; двигаем влево ROL R0 BVC 3$ INC R2 ; а тут отмечаем возникновение бита V 3$: SOB R5, 2$ ; на заданное количество бит BR EARX4$ 1$: ASR R0 ; двигаем вправо, тут переполнения не бывает ROR R1 SOB R5, 1$ ; на заданное количество бит BR EARX4$ ; ─────────────────────────────────────────────────────────────────────────── ; операция исключающее или. ; dd = dd xor R XOR$: CMP (SP)+, (SP)+ ;типа игнорируется? могли бы и заэмулировать. это не так и трудно BR EARX3$ ; ─────────────────────────────────────────────────────────────────────────── ; вход только для ASH/ASHC ; вход R0:R1 - результат ; R5 - флаги ; R2 - признак возникновения переполнения V EARX4$: ADC R5 ; ставим флаг C, если было TST R2 BEQ EARX1$ BIS #2, R5 ; ставим флаг V ; вход R0:R1 - результат ; R5 - флаги EARX1$: TST R0 ; смотрим знак результата BPL 1$ ; >= 0 BIS #10, R5 ; < 0 - ставим флаг N 1$: BNE EARX2$ ; идём туда TST R1 BNE EARX2$ BIS #4, R5 ; если R0:R1 == 0, ставим флаг Z EARX2$: MOV R0, @(SP)+ ; ст часть в R MOV R1, @(SP)+ ; мл часть в R+1 EARX3$: CMP (SP)+, #15 ; если регистр источник был SP и адресация была автоинкрементная BEQ 2$ ; то переход CALL RSTRS$ ; установим признаки из R5 и восстановим регистры RTI ; и выходим ; ─────────────────────────────────────────────────────────────────────────── 2$: CALL RSTRS$ ; установим признаки из R5 и восстановим регистры ; для автоинкрементной адресации со стеком, аргумент источника был в стеке, поэтому MOV 2(SP), 4(SP) ; сдвинем PS/PC на место аргумента MOV (SP)+, (SP) ; сделаем освобождение стека от аргумента RTI ; и выходим ; ─────────────────────────────────────────────────────────────────────────── mov #2000, R3 ;забытый, никому не нужный код call INIT movb R0, UNIT(R3) jmp BOOTN ; ─────────────────────────────────────────────────────────────────────────── .byte 377 ;простой мусор .byte 377 .byte 377 .byte 377 .byte 377 .byte 25 ; ─────────────────────────────────────────────────────────────────────────── ;начало отладчика DBGBGN: MOV #1000, SP ;стек в начальное значение MOV PC, R0 ADD #DBGBGN-., R0 MOV R0, @#4 ;вектор 4 на начало себя MOV #25012, R0 ;'.'<12> EMT 16 ;выведем перевод строки SWAB R0 EMT 16 ;выведем приглашение - '.' DB1$: CLR R2 CLR R1 2$: CALL REPKEY ;принимаем код EMT 16 ;выведем его CMPB R0, #30 ;это забой? BNE 1$ ;нет TST R2 ;число вводили? BEQ DB1$ ;нет ASR R2 ;да - одну цифру уберем ASR R2 ASR R2 BR 7$ ;и на экране тоже, но потом зачем-то в R2 херим всё число. смысл забоя непонятен ; ─────────────────────────────────────────────────────────────────────────── 1$: CMPB R0, #'0 ;это не цифры? BLO 3$ ;нет CMPB R0, #'7 ;точно не цифры? BHI 3$ ;точно нет INC R1 ;а если все-таки да ASL R2 ;то добавим цифру к числу ASL R2 ASL R2 BIC #177770, R0 ADD R0, R2 BR 2$ ; ─────────────────────────────────────────────────────────────────────────── 3$: CMP R0, #100 ;а это не буква? BLO 4$ ;нет BIC #240, R0 ;да - сделаем из неё большую латинскую букву ;ну и дальше проверки на всякие отладочные команды 4$: CMPB R0, #'A ;это команда А? BNE 5$ ;нет TST R1 ;в R2 что-то есть? BEQ 6$ ;нету MOV R2, R3 ;есть - его сохраним в R3 BR DB1$ ; ─────────────────────────────────────────────────────────────────────────── 6$: MOV #'=, R0 ;выведем текущий адрес EMT 16 MOV R3, R2 CALL OUT8 ;выведем содержимое R2 MOV #12, R0 7$: EMT 16 BR DB1$ ; ─────────────────────────────────────────────────────────────────────────── 5$: CMPB R0, #'I ;это команда I? BNE 8$ TST R1 ;в R2 что-то есть? BEQ 9$ ;нету MOV R2, (R3) ;есть - поместим его по адресу BR DB1$ ; ─────────────────────────────────────────────────────────────────────────── 8$: CMPB R0, #', ;это команда запятая? BNE 10$ TST R1 ;в R2 что-то есть? BEQ 11$ MOV R2, (R3) ;есть - поместим его по адресу 11$: TST (R3)+ ;и переходим к следующему 9$: MOV (R3), R2 ;возьмем содержимое по адресу CALL OUT8 ;и выведем его BR DB1$ ; ─────────────────────────────────────────────────────────────────────────── 10$: CMPB R0, #'- ;это команда минус? BNE 12$ TST R1 ;в R2 что-то есть? BEQ 13$ MOV R2, (R3) ;есть - поместим его по адресу 13$: TST -(R3) ;и переходим к предыдущему BR 9$ ; ─────────────────────────────────────────────────────────────────────────── 12$: CMPB R0, #'G ;это команда G? BNE DB2$ TST R1 ;в R2 что-то есть? BEQ DB2$ CALL (R2) ;есть - значит это адрес перехода, сходим по адресу BR DB1$ ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; приём кода с клавиатуры с автоповтором REPKEY: MOV #232, R0 ;переключение режима индикации курсора TSTB @#56 ;курсор погашен? BEQ 1$ ;да EMT 16 ;нет - погасим 1$: MOV #4000, R0 ;небольшая задержка SOB R0, . MOV #20000, R0 2$: BIT #100, @#177716 ;клавиша нажата? BNE 3$ ;нет TSTB @#2 ;нажата, задержку делать? BNE 4$ ;нет SOB R0, 2$ ;да - чуть задержимся INCB @#2 ;и больше задержку делать не надо 4$: MOV @#104, R0 ;вернем код последнего нажатого символа RETURN 3$: EMT 6 ;клавиша не нажата - ждем принятия кода CLRB @#2 ;установим флаг, что надо будет сделать задержку RETURN ; End of function REPKEY ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ;вывод числа из R2, с уничтожением содержимого OUT8: MOV #6, R4 CLR R0 1$: ASL R2 ROL R0 ADD #'0, R0 EMT 16 CLR R0 ASL R2 ROL R0 ASL R2 ROL R0 SOB R4, 1$ MOVB #40, R0 EMT 16 RETURN ; End of function OUT8 ; ─────────────────────────────────────────────────────────────────────────── ;продолжение отладчика DB2$: CMPB R0, #'K ;это команда K? BNE 1$ JMP @#100274 ;да - выход в монитор ; ─────────────────────────────────────────────────────────────────────────── 1$: CMPB R0, #'B ;это команда B? BNE 2$ CALL @#START ;да - загрузимся с дисковода MOV #'?, R0 ;не смогли загрузиться EMT 16 ;выведем CLR R2 BISB @#ERRFDD, R2 ;код ошибки CALL OUT8 JMP DBGBGN ;и начнем все сначала ; ─────────────────────────────────────────────────────────────────────────── 2$: CMPB R0, #'L ;это команда L? BNE 3$ MOV PC, R1 ADD #AA-., R1 ;спросим адрес CLR R2 EMT 20 CALL @#100472 ; ввод восьмеричного числа с клавиатуры(коды меньше 60 и больше 67 не учитываются, число в R5) BR 5$ ; ─────────────────────────────────────────────────────────────────────────── .BYTE 0 .BYTE 0 AA: .ASCIZ <12>"A= " .ASCIZ <12>"L= " .EVEN ; ─────────────────────────────────────────────────────────────────────────── 3$: CMPB #'S, R0 ;это команда S? BEQ 4$ JMP DB5$ ; ─────────────────────────────────────────────────────────────────────────── 4$: MOV PC, R1 ;да - спросим адрес SUB #-AA+., R1 CLR R2 EMT 20 MOV R1, -(SP) CALL @#100472 ; ввод восьмеричного числа с клавиатуры(коды меньше 60 и больше 67 не учитываются, число в R5) MOV R5, @#322 MOV (SP)+, R1 ;спросим длину CLR R2 EMT 20 CALL @#100472 MOV R5, @#324 ;заполняем блок параметров MOV #2, @#320 MOV #326, R1 BR DB4$ ;и идем записывать ; ─────────────────────────────────────────────────────────────────────────── BR DB3$ ;бесполезная команда ; ─────────────────────────────────────────────────────────────────────────── 5$: CALL @#100536 ; загрузка с магнитофона DB3$: JMP DBGBGN ; ─────────────────────────────────────────────────────────────────────────── DBGTTL: .ASCIZ "DEBUG MODE,H = HELP!"<12> .EVEN ; ─────────────────────────────────────────────────────────────────────────── ;отладчик DEBUGR: MOV PC, R1 ;выведем сообщение, что мы в отладчике SUB #-DBGTTL+., R1 CLR R2 EMT 20 ;внезапно только для БК10 JMP DBGBGN ; ну и поёдем отлаживать ; ─────────────────────────────────────────────────────────────────────────── DB4$: CALL @#100552 ; операции с магнитофоном BR DB3$ ; ─────────────────────────────────────────────────────────────────────────── DBGHLP: .ASCIZ <12>"B A L S G , K"<12> .EVEN ; ─────────────────────────────────────────────────────────────────────────── DB5$: CMPB #'H, R0 ;это команда H? BEQ DB6$ ;да - пойдем выведем справку JMP DB1$ ;а если нет - начинай все сначала ; ─────────────────────────────────────────────────────────────────────────── .WORD 0 ;впустую потраченное слово ; ─────────────────────────────────────────────────────────────────────────── JL5$: CMPB @#177717, #300 ;это БК11(М)? BNE JL9$ ;нет, это БК10 MOV R0, -(SP) ;сохраним номер привода, с которого надо загружаться MOV #FDDTTL, R0 ;начало текста CMP @#156616, #45440 ;это БК11? BEQ JL8$ ;да - выведем текст по БК11цатишьи EMT 64 ;нет - это БК11М - выводим текст по БК11цатиМшьи JL7$: MOV (SP)+, R0 ;восстановим номер привода JL2$: JMP JL6$ ;пойдем загружаться с приводов JL8$: EMT 20 ;выводим текст по БК11цатишьи JL11$: BR JL7$ JL9$: MOV R0, -(SP) ;для БК10 текст выводится вот так MOV #FDDTTL+1, R1 ;начало текста CLR R2 EMT 20 ;выведем текст по БК10тишьи BR JL10$ ;и пойдем добавим перевод строки еще ; ─────────────────────────────────────────────────────────────────────────── .BYTE 0 FDDTTL: .ASCIZ <15><12>"FDD/HDD BIOS 327 V12 (c) JD/DWG!" .EVEN ; ─────────────────────────────────────────────────────────────────────────── JL1$: BIT @#177712, #20 ; таймер уже работает? BNE JL2$ ; да - пойдем дальше CMP @#177706, #-1 ; значение инициализации таймера -1? (подразумевается, что оно выставляется при инициализации проца) BNE JL3$ ; нет - сходим-ка туда JL4$: MOV #160, @#177712 ; включаем таймер BR JL5$ ; и пойдем выводить текст о себе ; П/п загрузки которая кроме загрузки делает разные другие действия ; а так же бессмысленно скачет туда-сюда с целью запутать исследователя. ; Либо авторы просто не знали про существование ассемблеров BOOT: MOV #160000, R1 ; посчитаем контрольную сумму драйвера MOV #BOOT-START-2, R2 ASR R2 CLR R3 1$: ADD (R1)+, R3 ADC R3 SOB R2, 1$ CMP R3, #145566 ;если вот такая BEQ JL1$ ; - значит всё хорошо .WORD 177777 ;иначе - вот вам. получите враги народа ; ─────────────────────────────────────────────────────────────────────────── ; непонятно зачем вклинившийся сюда кусок отладчика DB6$: MOV #DBGHLP, R1 ; "\nB A L S G , K\n" CLR R2 EMT 20 ;выведем справку по командам отладчика JMP DB1$ ; ─────────────────────────────────────────────────────────────────────────── .WORD 0 ;еще одно впустую потраченное слово ; ─────────────────────────────────────────────────────────────────────────── ;попадая сюда, R0 == 0 JL6$: CALL BOOTC ;пробуем загрузиться с привода 0 MOV #1, R0 ;потом попробуем с привода 1 CALL BOOTC MOV #2, R0 ;потом - с привода 2 JMP BOOTC ; ─────────────────────────────────────────────────────────────────────────── .WORD 0 ;просто лишнее слово ; ─────────────────────────────────────────────────────────────────────────── JL10$: MOV #12, R0 ;выведем EMT 16 ;перевод строки BR JL11$ ; ─────────────────────────────────────────────────────────────────────────── ; запускаем прочитанный загрузчик RUNLDR: MOV #1000, SP ; Установим стек CALL @#1000 ; Запускаем загрузчик HALT ; останов, если загрузчик вместо того, чтобы дальше работать, вернулся сюда ; ███████████████ S U B R O U T I N E ███████████████████████████████████████ ; кусок п/п SETPTR: - настройки указателя FLGPTR SETPT1: BIC #177774, FLGPTR(R3) ; Отбросим лишние биты ADD R3, FLGPTR(R3) ; Прибавим базовый адрес FLGTAB RETURN ; ─────────────────────────────────────────────────────────────────────────── JL3$: CMP @#177710, #-1 ; а значение таймера -1? BEQ JL4$ ; да - пойдем включим таймер, а потом выведем текст CMP @#177710, #60000 ; а может, значение такое BHI JL4$ ; если больше, пойдем включать таймер, а потом выведем текст MOV #160, @#177712 ;иначе всё равно включим таймер BR JL2$ ;но совершим хитрый кульбит, в результате которого продолжим загрузку .END