[<>] ~~TOC wide~~ ====== Приложение 4. Пример организации связей с языком MSX BASIC. "Универсальное меню" ====== Приведенная ниже программа на языке [[msx:basic:]] вызывает подпрограмму на языке ассемблера и передает ей адрес строкового массива, используя функцию ''VARPTR''. Массив представляет собой меню. Строка массива, начинающаяся с символа пробел, считается комментарием. Пустая строка (%% "" %%) является признаком конца меню. Ассемблирование в системе [[msx:duad:duad|]] описано [[01#n121|здесь]]. Подпрограмма на ассемблере рисует на экране (параметры ''SCREEN 0:WIDTH 80'' — устанавливаются заранее) окно, размеры которого определяются программно, исходя из параметров переданного массива (Y — по количеству элементов массива, т.е. до пустой строки, X — по наиболее длинной строке). Координаты окна программа на языке [[msx:basic:]] передаёт функцией ''LOCATE X,Y'', используя тот факт, что интерпретатор записывает значения X и Y в системные ячейки. Запуск программы: run"wnd.bas" Строка-курсор устанавливается на первый возможный вариант выбора меню (комментарии пропускаются). Эту строку можно перемещать в пределах окна с помощью клавиш вверх/вниз. Выбор осуществляется нажатием клавиши ввод. Выбранная строка отмечается галочкой, и программе на языке [[msx:basic:]] возвращается её номер. Если в момент выбора были нажаты клавиши CTRL+Stop, возвращается ноль. Обратите внимание: - Пользователь [[msx:basic:]] должен сам следить за размещением окна на экране. - В меню должен быть хотя бы один возможный выбор. {{wnd.zip|Архив с готовыми файлами}} * ''wnd.asm'' — исходный код программы на ассемблере * ''wnd.lst'' — листинг программы на ассемблере * ''wnd.bas'' — программа на [[msx:basic:]] * ''wnd.obj'' — оттранслированная программа на ассемблере ===== 1. BASIC-программа ===== {{wnd.bas|}} 0 rem (c) 1990 И.Бочаров 10 CLEAR 200,&HDC00 ' Резервируем память для подпр. 20 DIM A$(17) ' Определяем массив-меню 30 BLOAD"wnd.obj" ' Загружаем подпрограмму 40 VDP(13)=&HA4:COLOR = (4,0,0,4)' Устанавливаем цвета окна 50 VDP(14)=&HF0 60 DATA " Select:"," ===============",Brief,Full,Info,Tree, On/Off ^F1," ---------------",Name,Extension,Size,Time, UnsORted," --------------",Help infORmation,Exit to main menu,"" 70 FOR I=0 TO 16 ' Считываем меню 80 READ A$(I) 90 NEXT 100 DEFUSR=&HDC00 ' Адрес подпрограммы-меню 110 DEFUSR1=&HDC03 ' Подпрограмма очистки экрана 130 : 140 LOCATE 2,1 ' Устанавливаем координаты окна 150 GOSUB 230 ' Вызываем подпрограмму 160 LOCATE 20,6 170 GOSUB 230 180 LOCATE 26,0 190 GOSUB 230 200 GOTO 140 ' Зацикливаемся 210 : 230 A=USR1(0) ' Очищаем цвета на экране 240 A=USR(VARPTR(A$(0))) ' Вызываем подпрограмму 250 IF A THEN LOCATE 75,22:PRINT A;:RETURN ELSE CLS:PRINT USR1( ""):END ' Обрабатываем результат 260 rem The end. ===== 2. Подпрограмма на языке ассемблера ===== {{wnd.asm}} ; (c) 1990 by IgOR BOCHAROV. ; ;--------------- Константы и адреса --------------------------- ; cr EQU 13 ; Клавиша "Конец выбора" Up EQU 30 ; Клавиша вверх Down EQU 31 ; Клавиша вниз TheBeg EQU 0DC00h ; Отсюда наша программа трансл. GetHL EQU 2F8Ah ; Подпрограммы передачи значен. PutHL EQU 2F99h Base1 EQU 0F3B5h ; Здесь хран. адрес табл. цвет. CsrX EQU 0F3DDh ; Сюда LOCATE пишет X CsrY EQU 0F3DCh ; А сюда Y RdVram EQU 004Ah ; Чтение из VRAM WrVrHL EQU 004Dh ; Запись во VRAM SetWrt EQU 0053h ; Установка VDP для записи ChSns EQU 009Ch ; Опрос состояния буфера клав. ChGet EQU 009Fh ; Взятие символа из буфера BreakX EQU 00B7h ; Опрос + ;-------------------------------------------------------------- ORG TheBeg ;------------------- Точки входа ------------------------------ JP Menu ; Универсальное меню JP ClMenu ; Очищение таблицы цветов ;-------------- Вычисляем размер окна ------------------------- Menu: CALL GetHL ; [HL] - адрес первого элемента PUSH HL ; Адрес первого элемента LD IY,Variab ; Адрес блока переменных XOR a ; Инициируем переменный LD (IY+dy),a ; Число строк LD (IY+dx),a ; dx l001: LD a,(HL) ; Длина очередной строки AND a ; Конец? JR Z,l002 ; Если да, то выходим INC (IY+dy) ; Увеличиваем число строк INC HL ; + длина INC HL ; + адрес INC HL CP (IY+dx) ; Больше dx? JR C,l001 ; Если нет, то dx не меняем LD (IY+dx),A JR l001 ;--------------- Вычисляем координаты окна -------------------- l002: LD HL,(CsrY) ; x y DEC H DEC L LD C,H ; x LD B,0 LD H,B ADD HL,HL ; * 2 ADD HL,HL ; * 4 ADD HL,HL ; * 8 ADD HL,HL ; * 16 LD E,L LD D,H ADD HL,HL ; * 32 ADD HL,HL ; * 64 ADD HL,DE ; * 80 ADD HL,BC EX DE,HL INC (IY+dx) ; dx INC (IY+dx) ; dx LD b,(IY+dx) ; dx LD c,(IY+dy) ; dy PUSH DE ; Для печати текста ;------------------- Рисуем окно ------------------------------ ; [DE] - x + y*80, [b] - dx, [c] - dy PUSH BC PUSH DE ; Для подсветки ; Рисование окантовки окна LD a,17h ; ─ LD HL,1819h ; ┌ ┐ CALL WndL01 WndL02: LD a,' ' ; ' ' LD HL,1616h ; │ │ CALL WndL01 DEC c JR NZ,WndL02 LD a,17h ; ─ LD HL,1A1Bh ; └ ┘ CALL WndL01 ;--- Теперь подсвечиваем ------------- NEXtColORInWindow: POP HL ; x,y POP DE ; dx,dy INC D INC D INC D ; dx = dx + 4 INC D INC E ; dy = dy + 2 INC E WndLp1: PUSH DE PUSH HL CALL FillColORTable ; Заполняем строку POP HL LD DE,80 ; [y] = [y] + 1 ADD HL,DE POP DE DEC E ; [dy] = [dy] - 1 JR NZ,WndLp1 ;------------------- Пишем текст в окне ----------------------- POP HL LD DE,80*1+3 ; Откуда начать писать ADD HL,DE LD D,H LD E,L EX (SP),HL ; Адрес первой строки l003: LD A,(HL) ; Длина очередной строки AND A ; Конец? JR Z,l004 ; Если да, то выходим LD B,A ; Текущий dx PUSH HL PUSH DE DI LD A,E ; Младший байт адреса OUT (99h),A LD A,D ; затем старший байт OR 40h ; выставив 6 бит в 1 OUT (99h),A INC HL LD A,(HL) ; Получаем адрес строки INC HL LD H,(HL) LD L,A LD C,98h ; загрузить номер порта VDP OTIR ; вывести блок EI POP HL LD DE,80 ADD HL,DE EX DE,HL ; [DE] - следующий адрес POP HL INC HL INC HL INC HL JR l003 ; Повторяем вывод ;------------------- Выбор в меню ----------------------------- l004: POP HL ; Адрес первой строки LD C,0 ; Текущее состояние LD D,C LD E,D l007: PUSH HL l008: ADD HL,DE CALL RdVram CP ' ' ; Пробел? JR NZ,l006 LD DE,80 JR l008 l006: POP DE CP 17h ; ─? JR NZ,l009 EX DE,HL DEC C l009: INC C PUSH BC ; Состояние PUSH HL ; Стираем засветку LD D,(IY+dx) DEC HL CALL ClearColORTable POP HL POP BC ;------------------- InputKey: CALL BreakX JR NC,Input LD C,0 JR Exit Input: CALL ChSns ; Есть что-нибудь в буфере? JR Z,Inputkey CALL ChGet ; Берем символ ;------------------- CP cr JR NZ,CheckUp LD A,'√' DEC HL CALL WrVrHL Exit: LD H,0 LD L,C ; Текущее состояние JP PutHL ;------------------- CheckUp: CP Up JR NZ,CheckDown LD A,C DEC A ; А можно ли наверх? JR Z,InputKey LD C,A PUSH BC PUSH HL LD D,(IY+dx) DEC HL CALL FillColORTable ; Стираем курсор POP HL LD DE,-80 ; Смещение на строку вверх l010: ADD HL,DE ; Новая позиция курсора CALL RdVram CP ' ' JR Z,l010 ; Сканируем пробелы LD D,(IY+dx) PUSH HL DEC HL CALL ClearColORTable POP HL POP BC JR InputKey ;------------------- CheckDown:CP Down JR NZ,InputKey PUSH HL LD D,(IY+dx) PUSH BC DEC HL CALL FillColORTable ; Стираем курсор POP BC POP HL LD DE,80 JR l007 ;-------------------------------------------------------------- WndL01: PUSH BC PUSH DE PUSH AF PUSH BC LD A,' ' CALL WrVram LD A,H CALL WrVram POP BC POP AF CALL FillVm LD A,L CALL WrVram LD A,' ' CALL WrVram POP HL LD BC,80 ADD HL,BC EX DE,HL POP BC RET ;-------------------------------------------------------------- ; Рисует цветную линию по координатам и размеру ; [HL] - y *80+ x; [d] - dx ; MoDIfy: AF, BC, DE, HL FillColORTable: LD E,10000000b CALL GetMaskColOR ; Получить адрес и маску WndLp4: CALL RdVram ; считать текущий байт WndLp3: OR E DEC D ; [dx] = [dx] - 1 JP z,WrVrHL RRC E ; Следующая маска JR NC,WndLp3 ; Если не вышли из байта, то не ; пишем CALL WrVrHL ; Иначе записываем INC HL ; Следующий адрес VRAM JR WndLp4 ;-------------------------------------------------------------- ; Очищает цветную линию ; [HL] - y *80+ x; [d] - dx ; MoDIfy: AF, BC, DE, HL ClearColORTable: LD E,01111111b CALL GetMaskColOR ; Получить адрес VRAM и маску WndLp5: CALL RdVram ; считать текущий байт WndLp6: AND E DEC D ; [dx] = [dx] - 1 JP Z,WrVrHL RRC E ; Следующая маска JR C,WndLp6 ; Если не вышли из байта, то не ; пишем CALL WrVrHL ; Иначе записываем INC HL ; Следующий адрес VRAM JR WndLp5 ;-------------------------------------------------------------- ; По координатам выдает адрес VRAM и маску ; [HL] - y*80 + x; ; [HL] - адрес Vram; [e] - маска GetMaskColOR: LD A,L ; Получаем из коор. физ. адрес LD B,3 SRL H ; [HL] = [HL] / 8 RR L DJNZ $-4 LD BC,(Base1) ADD HL,BC ; Физический адрес VRAM CPl ; [a] = not([a]) AND 00000111b ; Бит, который надо установить LD B,A INC B RLC E DJNZ $-2 RET ;-------------------------------------------------------------- ; Очистка цветной таблицы ClMenu: LD DE,(Base1) LD B,24*10 XOR A ;-------------------------------------------------------------- ; заполнение VRAM const [a], len [b], adr [DE] FillVm: DI PUSH AF LD A,E OUT (99h),A LD A,D OR 40h OUT (99h),A POP AF EX (SP),HL EX (SP),HL OUT (98h),A INC DE djNZ $-3 ; повторить, если надо RET ;-------------------------------------------------------------- WrVram: EX DE,HL CALL WrVrHL EX DE,HL INC DE RET ;-------------------------------------------------------------- Variab EQU $ dy EQU 1 dx EQU 2 END ---- [<>] {{tag>MSX Book_apguidefb on_github}}