
С УКНЦ я познакомился в 1998 году, когда устроился преподавателем в одну школу. Был класс и минимум документации и софта - игрушки, BASIC и системные диски. Через два года я поменял работу на другую - зряплата побольше и компы поинтереснее. Сейчас нашлось свободное время, и я занялся изучением неведанного компьютера. Скачал эмулятор UKNCBTL, образы дисков, и занялся программированием. Облом первый: при запуске эмулятор требует DLL.Взял прежнюю версию UKNCBTL-609-exe.zip, запустил. Для разминки попробовал классическую задачу печати "Hello,world!". Запустил редактор EDIK, набрал найденный исходник и получил второй облом: в эмуляторе не удалось переключиться на русскую раскладку. Перепробовал массу вариантов, плюнул и ввел текст транслитом. Возникает вопрос - как сохранить текст и выйти? Взял утилиту rt11dsk, извлек из образа EDIK.TXT и надолго залип, разбираясь с кодировкой. На помощь пришла Алиса AI, которая накидала исходник конверсии текста koi7conv. Перекодировал текст, нашел описание и не сумел сохранить текст и выйти из программы. Алиса снова подсказала:
В текстовом редакторе EDIK для УКНЦ выход из программы зависит от того, сохранили ли вы изменения.
Выход с сохранением — нажмите К1 + Е (клавиша К1 в сочетании с буквой Е).
Выход без сохранения — нажмите К1 + Q (клавиша К1 плюс буква Q).
(Эти клавиши работают не во всех версиях программы).
Сохранил текст, ввел команды HELLO HELLO LINK HELLO и получил результат:
Исходный тест:
.MCALL .TTYOUT, .EXIT
HELLO:
MOV #MESSAGE, R1
L1:
MOVB (R1)+,R0
BEQ DONE
.TTYOUT
JMP L1
DONE:
.EXIT
MESSAGE:
.ASCIIZ /Priwet,mir!/
.END HELLO
Последняя строка должна быть пустая, чтобы компиляция прошла.
Теперь я стал искать информацию о графическом режиме, получил массу сведений и не сумел изобразить на экране хоть что-нибудь. Задал на вопрос на гф и, как ожидалось, получил ответ:
ссылка на документации, которые я уже видел. Позже получил пример работы программы:
START:
MOV #10000,R3
CLR R0
MOV #100000,@#176640
CYCLE:
MOV R0,@#176642
INC R0
INC @#176640
SOB R3,CYCLE
HALT
.END START
Стал разбираться с кодом. Выглядит это так:
В УКНЦ дисплейный контроллер имеет регистры, отображённые в адресное пространство ввода-вывода:
176640 (восьмеричное) — регистр адреса видеопамяти. Записав сюда значение, вы указываете контроллеру, с какого слова видеопамяти начинать работу.
176642 (восьмеричное) — регистр данных видеопамяти. Запись сюда помещает слово в видеопамять по текущему адресу.
А пишешь адрес видеопамяти (если сильно не вдаваться в кучу подробностей работы ПП таблицы видеострок и прочего) - короче пишешь туда адрес видеопамяти вида 100000 (восьмеричное)
mov #100000, @#176640
После этого в регистре 176642 появляется слово из двух байт планов зеленого и красного цвета которое представляет собой 8 пикселей экрана (там ещё есть и синий план, ну да фиг с ним пока-что).
Если ты запишешь после такого в регистр 176642 какое-нибудь слово (после того как установил в 176640 адрес экрана, начало с 100000 считай) - у тебя обновится 8 пикселей на экране.
Понятно что в УКНЦ есть ещё и скролл экрана и много всяких вещей, но для начала "и так пойдет" для понимания.
Если более нормально - заносишь в 176640 адрес/2 памяти которая "у центрального процессора". Почему и 100000 ибо реальный адрес будет 200000 (хотя такое адресовать процессор 16 бит не может). После чего ты манипулируешь (читаешь/пишешь) регистром 176642 в котором будет по считыванию находиться значение этой памяти, ну а по записи ты это значение обновишь. Сама видеопамять УКНЦ может в зави-ти от прихотей программиста располагаться да где угодно, но там надо много курить мануалы и лезть в периферийный процессор.
Например тебе надо обновить 8 бит экрана в восьмой строке с самого начала (опять-же я опускаю скролл экрана). Каждая строка это 80 слов видеопамяти в ЦП (опять-же не говорю про таблицу строк, пусть будет дефолт). Значит надо занести 120*10+100000 (я в восьмеричном пишу) в регистр 176640 (помножь на 2 ибо туда заносится деленное на 2 - узнаешь что адрес будет выше 200000) и затем записать (можешь и считать кст) что-нибудь (слово) в 176642
Я написал программу по-другому
START:
MOV R0,@#176640
MOV #80.,R1
LL:
MOV #125125,@#176642
INC @#176640
SOB R1,LL
WW:
BR WW
.END START
На гф последовало объяснение - нужно сбросить рулон, бла-бла-бла. Я решил попробовать очистку экрана. Очистилось, но результат стал иным:
А если сбросить этот самый рулон?
.MCALL .TTOUT, .EXIT
START:
;CLS
MOV #033, R0
.TTOUT
MOV #12., R0
.TTOUT
; https://emuverse.ru/wiki/УКНЦ_RAM.LST_app
MOV #033, R0
.TTOUT
MOV #131, R0 ; same 231
.TTOUT
MOV #32., R0
.TTOUT
MOV #32., R0
.TTOUT
; https://textarchive.ru/c-1107613-p6.html
; MOV #ST,R1
; EMT 46
MOV #100000,R0
MOV R0,@#176640
MOV #80.,R1
LL:
MOV #125125,@#176642
INC @#176640
SOB R1,LL
WW:
BR WW
;ST:
; .BYTE 033,12.,033,131,32.,32.,0,0
.END START
В начальном коде много лишнего с выводом кодов и я решил разобраться с печатью позже. Но мне надоел этот нудный процесс - загрузка, редактирование, компиляция и запуск, поэтому я вернулся к кросс-средствам - rt1dsk и
эмулятор RT-11. Теперь компиляция делается пакетным файлом:
del pr.sav
del pr.obj
rt11 macro pr /list:pr.lst
rt11 link pr
rt11dsk d sys1002.dsk pr.sav
rt11dsk a sys1002.dsk pr.sav
pause
Дело пошло быстрее, я вернулся к решению задуманной задачи. Но сначала я проверю неясное утверждение: порядок точек задается обратно:
.MCALL .PRINT,.TTOUT, .EXIT
START:
;CLS
.PRINT #MSG
MOV #100000,@#176640
MOV #1,@#176642
LL:
BR LL
.END START
На экране появилась точка на левом углу - я записал значение 1.
Написал код, пострадал с отладчиком. Нужно выбрать пункт меню Debug->Debug views. Минимум команд доступен в окне Debug console. И все же я получил результат:
.MCALL .PRINT,.TTOUT, .EXIT, .TTYIN
START:
;CLS
.PRINT #MSG
MOV #100000,@#176640
; MOV #1,@#176642
; .RADIX 8
; --- Отправить ESC (033) напрямую в порт, минуя фильтр ---
ESC1: TSTB @#177564 ; бит 7 = готовность ПП
BPL ESC1 ; ждём
MOVB #033, @#177566 ; записываем ESC в регистр данных
.PRINT #MSG2
CLR R0 ; Y
YLP:
CLR R1 ; X
XLP:
CLR R3 ; bit value
MOV #1.,R4
BLP:
MOV R0,R2
XOR R1,R2 ; X!Y
; N%9
MOD9:
CMP R2,#9.
BLT DONE
SUB #9., R2
BR MOD9
DONE:
TST R2
BNE ZERO
ADD R4,R3
ZERO:
INC R1
ADD R4,R4
CMP #256.,R4
BNE BLP
MOV R3,R4
SWAB R3
ADD R4,R3
MOV R3,@#176642
INC @#176640
CMP #640.,R1
BNE XLP
INC R0
CMP #280.,R0
BNE YLP
WW:
BR WW
MSG2: .ASCIZ /%!0/ ; Если дальше понадобится, например, вернуть курсор обратно (ESC %!3)
MSG:
.BYTE 033,014,033,131,040,040
.BYTE 0
.END START
Курсор убран командой печати управляющих символов ESC%!0. Программа рисует картинку довольно медленно.
Остался еще один неясный момент. У компьютера УКНЦ джва процессора на борту: ЦП(центральный процессор) и ПП(периферийный процессор). ПП занимается работой с экраном и периферией. Я попытался разобраться с принципом работы, но получилось не сразу. Только долгий чат с одной гражданкой подсказал пример:
.TITLE LOADPP
.MCALL .PRINT, .EXIT
; === Регистры канала К2 ===
K2STS = 176674 ; Регистр состояния канала К2
K2DAT = 176676 ; Регистр данных канала К2
START:
; Длину берём из PPLEN, а не из #()
MOV PPLEN, R0 ; Загрузить длину из памяти
ASR R0 ; Байты -> слова
MOV R0, LENGTH
; ... дальше отправка МП ...
; --- Этап 1: Запрос памяти в ПП ---
MOVB #1, COMAND ; Команда "ВЫДЕЛИТЬ ПАМЯТЬ"
MOV PPLEN, LENGTH ; Длина программы в словах
JSR PC,SENDMP ; Послать МП в ПП
TSTB MASSIV ; Проверить результат
BNE ERR ; Не 0 — ошибка
; --- Этап 2: Запись программы в ПП ---
MOVB #20, COMAND ; Команда "ЗАПИСЬ"
MOV PPADR, ADR$CP ; Откуда в ОЗУ ЦП
MOV PPLEN, LENGTH ; Сколько слов
JSR PC,SENDMP ; Послать МП в ПП
TSTB MASSIV
BNE ERR
; --- Этап 3: Запуск программы в ПП ---
MOVB #30, COMAND ; Команда "ПУСК"
; ADR$PP уже содержит адрес, который вернул ПП
; на этапе 1 (выделение памяти)
JSR PC,SENDMP
TSTB MASSIV
BNE ERR
; ... здесь можно ждать завершения ...
; --- Этап 4: Освобождение памяти ---
MOVB #2, COMAND ; Команда "ОСВОБОДИТЬ ПАМЯТЬ"
JSR PC,SENDMP
TSTB MASSIV
BNE ERR
.EXIT
ERR: .PRINT #ERRMSG
.EXIT
ERRMSG: .ASCIZ /Ошибка загрузки в ПП/
; === Подпрограмма передачи МП в ПП через К2 ===
.EVEN ; Выровнять на чётную границу
SENDMP:
; Сначала инициализация канала: 4 байта (0,0,0,377)
MOV #4, R1 ; 4 байта инициализации
MOV #INITB, R0 ; Адрес массива инициализации
1$: TSTB @#K2STS ; Ждать готовности канала
BPL 1$
MOVB (R0)+, @#K2DAT ; Послать байт инициализации
SOB R1, 1$
; Теперь послать адрес МП и стоповый элемент
MOV #ADRMAS, R0 ; R0 -> на поле ADRMAS в МП
MOV #5, R1 ; 4 байта адреса + 1 байт (стоп)
2$: TSTB @#K2STS
BPL 2$
MOVB (R0)+, @#K2DAT ; Послать байт
SOB R1, 2$
; Ждать, пока ПП примет и обработает МП
3$: TSTB @#K2STS ; Ждать подтверждения
BPL 3$
RTS PC
INITB: .BYTE 0, 0, 0, 377 ; Байты инициализации канала
; === Массив параметров ===
MASSIV: .BYTE 0 ; Байт ошибки (заполняет ПП)
COMAND: .BYTE 0 ; Код команды
TYPDEV: .WORD 32 ; Устройство = ПП
ADR$PP: .WORD 0 ; Адрес в ОЗУ ПП (заполняет ПП)
ADR$CP: .WORD 0 ; Адрес в ОЗУ ЦП
LENGTH: .WORD 0 ; Длина в словах
ADRMAS: .WORD MASSIV ; Адрес начала МП
.WORD 401 ; Стоповый элемент
; === Программа, которая будет загружена в ПП ===
SPP:
; Здесь — код, который будет работать в ПП.
; Программа должна быть перемещаемой (position-independent),
; т.к. адрес её размещения заранее неизвестен.
;
; Например — запись в видеопамять:
MOV #100000, @#177010 ; Адрес начала ВОЗУ в РА
MOV #0177777, @#177012 ; все биты в планах 0 и 1
MOV #0177777, @#177014 ; все биты в плане 2
RTS PC ; Возврат (завершение процесса)
EPP:
; === Параметры загружаемой программы ===
PPADR: .WORD SPP ; Адрес программы в ОЗУ ЦП
PPLEN: .WORD EPP-SPP ; Длина в словах
Ниже строчки "Empty bytes:" нарисована линия 8 точек.И пока на этом можно поставить паузу.
Файлы
тут. В интернетах найдется немало документации, чтобы забить голову.
Комментарии
Отправить комментарий