СОЗДАНИЕ ПЕРЕНОСИМЫХ ПРОГРАММ НА RW1P2 -------------------------------------- Copyright (c) 2001-2002, Alexander Shabarshin Ekaterinburg, Russia (shaos@mail.ru) http://robots.shaos.ru/pub/rw1p2.rar (0.5M) СОДЕРЖАНИЕ 1. ВВЕДЕНИЕ 2. ТРАНСЛЯТОР БИБЛИОТЕКИ СПРАЙТОВ 3. КОМПИЛЯЦИЯ БАЙТКОДА 4. ТРАНСЛЯЦИЯ КОДА ПРОЦЕССОРА 5. СОЗДАНИЕ РАБОЧЕГО КОДА 6. ПРОСТЫЕ ПРИМЕРЫ 7. ОСОБЕННОСТИ RW1P2 ДЛЯ ПРОГРАММИСТА 8. ВЗАИМОДЕЙСТВИЕ С СИСТЕМОЙ -------------------------------------- 1. ВВЕДЕНИЕ Идея программирования в стиле RW1P2 заключается в том, что мы используем компилятор RW1C для компиляции программы на языке RW1 в байт-код RW0, который в дальнейшем транслируется в код конкретного процессора и системы. Важным свойством байт-кода является возможность отладки программ на уровне байт-кода (для этого сейчас создается реализация виртуальной машины RW0 на языке RW1P2), что может помочь отлавливать ошибки не доходя до уровня машинных команд. Программой в духе Robot Warfare 1 Platform 2 является код как таковой плюс библиотека спрайтов, выводимых в те или иные места экрана по ходу выполнения программы. В таком стиле можно программировать простые плоские игры, тетрис например, и др. У меня есть огромное желание написать на RW1 визуализатор боя роботов для перенесения нашей игры (http://robots.shaos.ru) на поддерживаемые системы. Кроме того правила трансляции RW0 байткода в код конкретного процессора являются открытыми (файл __RULES в каталоге конкретного процессора). Каждый может внести свои изменения, что-то улучшить или даже поддержать новую систему или процессор. Трансляция осуществляется через язык ассемблера (в-принципе можно использовать и другой низкоуровневый язык). В переспективе ассемблирование будет происходить внутри самого транслятора. 2. ТРАНСЛЯТОР БИБЛИОТЕКИ СПРАЙТОВ SPR_COMP.EXE - программа для трансляции файлов спрайтов SPR в бинарные файлы BIN. В файле спрайтов спрайты представляются текстовыми строками, составленными из шестнадцатиричных чисел. Пока поддерживается лишь тип спрайтов 8x8-2/16, что значит размер спрайта 8 на 8 пикселов с использованием 2 цветов из 16 - для описания цвета символа и цвета фона. Пример строки из файла SPR: NAME DB #00,#11,#AA,#55,#00,#00,#00,#00,#F4 ;x В данном случае мы имеем 8 байт описания строк спрайта - один бит на пиксел и 1 байт атрибутов (4 старших бита - цвет фона, 4 младших бита - цвет символа). Кроме того имеется текстовый символ для замены спрайта на текстовых системах (например РАДИО-86РК). Этот символ пишется за символом комментария ; в той же строке. Если в этом же комментарии имеется символ '!', то байты описывающие спрайт инвертируются (полезно для систем, в которых используется только черно-белое представление, а переделывать спрайты нет возможности). Пример запуска транслятора библиотеки спрайтов: SPR_COMP -O#1000 -Sorion NAME.SPR -O опция для задания начального адреса библиотеки спрайтов в памяти целевого компютера, шестнадцатиричное значение пишется после символа #, без этого символа значение воспринимается как десятичное. -S опция задания системы цветности, по-умолчанию ORION (Орион-128). Кроме того есть системы SPECCY (для ZX-SPECTRUM) и SPEC (для компьютера Специалист). При использовании опции RADIO (Радио-86РК), бинарный файл создаваться не будет, заменяющие символы будут записаны непосредственно в файл определений RWI. Результатом работы программы будет бинарный файл BIN и файл определений RWI, который следует включить в создаваемую программу на языке RW1, использующую данную библиотеку. Имена этих файлов будут совпадать с именем файла SPR. В файле RWI для каждого спрайта из SPR будет создана запись в виде @имя_спрайта=числовой_идентификатор Эта строка является макроопределением для замены идентификатора спрайта в команде SET. Кроме того, в версии 1.2 (от 13 июня 2001 года) появилась опция -PALM, которая вместо одного BIN файла создает несколько файлов ресурсов, по одному на спрайт, с именами TbmpXXXX.bin, где XXXX - шестнадцатиричный индекс ресурса-пиктограммы (отсчитываются от начального адреса -O и увеличиваются на 1). Эти спрайты могут быть использованы совместно с ассемблером PILA для создания приложений для PalmOS. Для этого генерируется файл с расширением RES, описывающий созданные ресурсы. 3. КОМПИЛЯЦИЯ БАЙТКОДА В программу на языке RW1 нужно включить файл описаний RWI с помощью команды препроцессора +. Например имея файл SPR.RWI, полученный после компиляции утилитой SPR_COMP, мы включаем его следующим образом: ROBOT "First" AUTHOR "Name" +RW1_STD.RWI +SPR.RWI // включение файла описания спрайтов MAIN() { // код программы } Команды для использования спрайтов: SELECT x y - задает местоположение для вывода спрайта, SET n - выводит спрайт в текущее местоположение. Кроме того команды STEP,RIGHT,LEFT переопределены для управления текущим местом для вывода. В момент старта программы местоположение вывода располагается в левом верхнем углу - в точке (0,0). Направление движения - вправо. Компилируется программа компилятором-препроцессором RW1C версии не менее 2.0.6. Пример командной строки: RW1C -l -p first.rw1 При этом при компиляции будут создаваться файл результатов препроцессорной обработки с расширением RW! и файл листинга LST. 4. ТРАНСЛЯЦИЯ КОДА ПРОЦЕССОРА Трансляция осуществляется с помощью программы RW0COMP.EXE. У программы существует файл инициализации RW0COMP.INI в котором могут быть перечислены опции по умолчанию, которые могут быть переопределены из командной строки. Опции у программы следующие: -Pprocessor - целевой процессор, -Ssystem - целевая система, -Oaddress - адрес начала программы, -Baddress - адрес начала области переменных. Например RW0COMP -Pi8080 -Sorion -O#1000 -B#4000 FIRST.RW0 Или просто RW0COMP FIRST.RW0 если опции описаны в файле RW0COMP.INI, каждая в своей строке. Следует отметить, что для каждого процессора должен существовать файл LIB\processor\__RULES, где processor - имя процессора, а для каждой системы на таком процессоре должен существовать файл LIB\processor\system.A, где system - имя системы. Например для процессора I8080 определены четыре системы ORION, SPECCY, SPEC, RADIO в соответствующих А файлах. Ничего не мешает определить для системы SPECCY и родной процессор Z80 для более оптимального использования возможностей системы. В таком случае в каталоге LIB должен существовать каталог Z80 с файлом __RULES и специфическими системными функциями в файле SPECCY.A. В результате трансляции создается ассемблерный файл A, который в дальнейшем будет компилироваться самой утилитой RW0COMP, а пока, для процессора i8080 можно пользоваться утилитой RASM.EXE. 5. СОЗДАНИЕ РАБОЧЕГО КОДА После создания бинарных файлов спрайтов и программы их нужно соединить, это осуществляется с помощью утилиты ADDBIN. Формат использования: ADDBIN target.BIN source.BIN hex Например, если у нас есть спрайты SPR.BIN, оттранслированные с адреса #8000 и программа FIRST.BIN, оттранслированная с адреса #A000, то соединить их можно так: ADDBIN game.bin first.bin 8000 ADDBIN game.bin spr.bin A000 При этом будет создан файл GAME.BIN, совмещающий в себе два бинарных файла со смещением от начала #1000 и #4000. Если необходимо удалить лидирующие нули, то это можно сделать с помощью утилиты LEADZERO.EXE (если вы хотите далее использовать программу BIN2BRU, то лидирующие нули убирать не нужно). Для ZX можно сделать отдельные TAP файлы для каждого блока с помщью утилиты BIN2TAP, а потом слить их вместе с помощью COPY /B. Придется написать простую программку на ZX-бейсике для загрузки бинарных файлов с "ленты". Например: 10 LOAD "first" CODE 20 LOAD "spr" CODE 30 RANDOMIZE USR nnnn где nnnn - десятичное представление стартового адреса, с которого происходила кросскомпиляция. А можно объединить все в один BIN (как описано чуть выше) и сделать из него один TAP файл: BIN2TAP game.bin 8000 При этом программа загрузчик получится чуть проще, но, возможно, получившийся TAP файл будет больше по размеру. Для ОРИОНА с помощью утилит BIN2BRU и BRU2RKO можно сделать файлы BRU или RKO. Для Радио, Ориона, Специалиста можно сгенерировать RSS файл с помощью утилиты BIN2RSSA. Такой формат файлов понимается эмулятором EMU80 Виктора Пыхонина. В случае Специалиста можно воспользоваться эмулятором Александра Шевцова SPMX42. Для этого не нужно создавать RSS-файл. Вместо этого берется BIN файл и переименовывается в файл с расширением I80. Также создается текстовый файл с таким же именем файла и расширением CPU: 0000 0000 SPMX.ROM Здесь первые две строки - адрес загрузки и адрес старта, а третья строка - используемый файл с прошивкой ПЗУ. 6. ПРОСТЫЕ ПРИМЕРЫ Создадим пример программы на RW1, которая просто выводит спрайт в клетку (0,0). Для начала придумаем спрайт, который будем выводить: ######## => #FF # # => #81 # # # # => #A5 # # => #81 # # => #81 # ##### => #9F # # => #81 ######## => #FF Предположим, цвет точек будет черным, а фона - красным (т.е. #40). Создадим файл MYSPR.SPR библиотеки спрайтов: ;8x8-2/16 ;myspr.spr file MYSPR DB #FF,#81,#A5,#81,#81,#9F,#81,#FF,#40 ;@ Откомпилируем его для системы Орион: SPR_COMP -O#6000 -Sorion myspr.spr В результате мы получили два файла - бинарный MYSPR.BIN и текстовый MYSPR.RWI, состоящий из одной строки: @MYSPR=24576 Теперь напишем программу: // MY.RW1 robot "MY" author "Shaos" +myspr.rwi main() { select 0 0 // выбор клетки для вывода set @MYSPR // вывод нашего спрайта } Первым делом ее надо скопилировать: RW1C -p -l my.rw1 В результате получим выходной файл препроцессора MY.RW!, файл листинга MY.LST и нужный нам файл MY.RW0. Его-то нам и нужно транслировать дальше. RW0COMP -Pi8080 -Sorion -O#2000 -B#4000 my.rw0 Заметьте, что стартовым адресом мы назначили адрес #2000, а с адреса #4000 у нас будут располагаться переменные программы. Если вы сделали все правильно, то должен получиться файл на языке ассемблера MY.A, похожий на этот: \ RW0COMP.EXE http://robots.ural.net \ Copyright (c) 2001, Alexander Shabarshin (shaos@mail.ru) ORG #2000 @BASE EQU #4000 @REGS EQU 16448 @REG_X EQU 16448 @REG_Y EQU 16450 @REG_R EQU 16458 @REG_A EQU 16468 @REG_B EQU 16470 @REG_C EQU 16472 @REG_L EQU 16476 LXI_SP, @REGS \ SET _L_BASE AND _L_REGS LXI_H, _L_BASE LXI_D, @BASE PUSH_D CALL _DE_S LXI_D, @REGS CALL _DE_S POP_H \ ERASE FROM BASE TO REGS MVI_C, 0 _STD_1: MOV_M,C INX_H MOV_A,H CMP_D JNZ _STD_1 MOV_A,L CMP_E JNZ _STD_1 JMP _START \ DATA _L_BASE DW 0 _L_REGS DW 0 _NPLANE DW 0 _ANGLE DB 0 _COLOR DB 0 _START: \ *0x69 0x00 %w1 0x00 %w2 _j0000: MVI_A, #0000 STA @REG_X MVI_A, #0000 STA @REG_Y \ *0x6A 0x00 %w1 0x00 0x00 0x00 _j0007: LXI_H, #6000 CALL _SET \ *0x43 0x00 %w1 _j000E: JMP _j0000 \ *0xFF _j0012: +LIB\i8080\_STD +LIB\i8080\orion +LIB\i8080\_PLATF2 Этот файл можно отдавать на съедение ассемблеру RASM: RASM my.a Появление сообщения "Компиляция завершена", а также возникновение файлов MY.BIN и нового MY.LST, говорит нам, что создание кода прошло успешно. Теперь нам нужно объединить файлы MY.BIN и MYSPR.BIN. Вспомним стартовые адреса наших блоков кода и воспользуемся ADDBIN: ADDBIN mycode.bin my.bin 2000 ADDBIN mycode.bin myspr.bin 6000 Для эмулятора Ориона нам понадобится файл BRU: BIN2BRU mycode.bin 2000 Важно помнить, что код программы в памяти должен располагаться раньше, чем код спрайтов. Итак, мы получили файл MYCODE$.BRU, который можем запустить на исполнение: ORIONEMU mycode$.bru Стартовав MYCODE$, мы увидим смешную красную рожицу в левом верхнем углу экрана :) Усложним программу: // MY.RW1 robot "MY" author "Shaos" +myspr.rwi main() { dx = 20 dy = 10 for(xx=0,xx my_.tap $name=my $spr=myspr $aspr=D000 $abin=8000 $avar=E000 $syst=speccy $proc=i8080 spr_comp -S${syst} -O#${aspr} ${spr}.spr \del ${name}.rw0 rw1c -p -l ${name}.rw1 \need ${name}.rw0 \rename ${name}.lst ${name}.ls rw0comp ${name}.rw0 -P${proc} -S${syst} -O#${abin} -B#${avar} \need ${name}.a \del ${name}.lst rasm ${name}.a \need ${name}.bin \del ${name}_.bin addbin ${name}_.bin ${name}.bin ${abin} addbin ${name}_.bin ${spr}.bin ${aspr} leadzero ${name}_.bin bin2tap ${name}_.bin ${abin} \del ${name}.bin \del ${spr}.bin \del ${name}.lst \del ${name}.ls \del ${name}.a 7. ОСОБЕННОСТИ RW1P2 ДЛЯ ПРОГРАММИСТА В отличие от оригинального языка RW1, который используется для программирования боевых роботов, модификация RW1P2 имеет ряд отличий и добавлений. В версии 1.3 задействованы далеко не все регистры RW1: N - идентификатор передатчика сигнала после команды RECV (@KEYBOARD или @MOUSE), L - приемник по умолчанию значения выражения (например после (1+3) значение L будет 4), R - регистр случайных значений (в версии 1.3 обновляется во время команды RECV), T - некоторая характеристика времени (более точная реализация будет в следующих версиях), регистры для записи и чтения A,B,C - используются для передачи аргументов процедурам. Для опроса клавиатуры нужно выполнить: buff = 0 recv buff if(N==@KEYBOARD) { SAY "character &buff " ... } Лучше организовывать свою программу как цикл обработки сообщений. Кроме того полезно время от времени вызывать команду RECV для того, чтобы изменялись значения регистров R и T. Кроме того регистр R (случайное число от -32768 до 32767) будет менять свое значение при непосредственном присвоении var=R (но не в выражениях - var=R+1). Регистр T в будущих версиях будет обозначать количество десятых долей секунды, прошедших с момента старта программы. Это не будет работать для систем, не поддерживающих время, т.е. RADIO, SPEC, ORION. В случае SPECCY (ZX-Spectrum) это свойство будет поддержано. Важно помнить, что при вызове процедуры можно передавать лишь не более трех аргументов, потому что запись FUN(1,2,3) компилируется как A=1;B=2;C=3;CALL FUN. Если вы вызываете проуцедуру без параметров, то лучше писать CALL FUN, чем FUN(). Старайтесь не использовать односимвольные имена для переменных и меток, это может конфликтовать с именами регистров RW1. Описание головной функции MAIN должно предшествовать описанию всех остальных функций. Все объявляемые переменные являются глобальными, поэтому следите за именами. Переменные объявляются лишь внутри функций. Напомню, что объявлением переменной является простая запись с присвоением, например VAR=0. Допускается иначе использовать команду SAY. В обычном смысле она выводит строку с возможными значениями переменных в нижнюю часть экрана (можно переопределить - см.ниже). Если же в качестве первого символа указать обратный слеш '\', а в качестве второго восклицательный знак '!', то все остальное будет просто вставлено в генерируемый ассемблерный файл (т.е. это реализация машиннозависимого встроенного ассемблера). Пример: SAY "\!mov a,x ;in asm" -> mov a,x ;in asm Этого не рекомендуется делать, если вы желаете, чтобы ваша программа адекватно работала на разных платформах. Включение файлов RWI (функции-макросы) желательно производить до функции MAIN, а файлов RWL (библиотеки процедуры) после функции MAIN (см.пример ниже). Фигурная скобка { или } должна располагаться одна в строке. Для while, for, do обязательно должны указываться открывающие и закрывающие фигурные скобки. 8. ВЗАИМОДЕЙСТВИЕ С СИСТЕМОЙ Для взаимодействия с системой используется команда COMMAND. Для того, чтобы вы могли ее использовать, необходимо включить файл INC/RW1P2.RWI. Также для удобства можно использовать библиотеки LIB/*.RWL. Для RW1P2 версии 1.4 можно использовать следующие команды: @P2_TERMINFO - возвращает информацию о терминале. Использование - COMMAND @P2_TERMINFO. После вызова в регистрах находится следующая информация: A - размер экрана по горизонтали (в клетках), B - размер экрана по вертикали, C - цветовая модель (0 для текстового режима или количество битов цвета для графического). В случае использования LIB/P2TERM.RWL вызов будет выглядеть так - terminfo(0). @P2_SETSAY - установка параметров команды SAY "...". Использование - A=newx;B=newy;C=newcol;COMMAND @P2_SETSAY где newx и newy новые начальные координаты (в клетках) выводимой строки, а newcol - цвет текста в формате ОРИОН (4 старших бита - цвет фона, 4 младших бита - символа). В случае использования LIB/P2TERM.RWL вызов будет выглядеть так - termsetsay(newx,newy,newcol). @P2_SETATR - подмена атрибутов спрайта при выводе (это также изменяет цвет и в команде SAY). Использование - A=newcol;COMMAND @P2_SETATR где newcol - цвет в формате ОРИОН (4 старших бита - цвет фона, 4 младших бита - символа). Срабатывает только для двухцветных спрайтов. В случае использования LIB/P2TERM.RWL вызов будет выглядеть так - termsetatr(newcol). Для возвращения в режим без подмены - termsetatr(0). @P2_TERMCLR - очистка терминала. В регистре A должен быть передан идентификатор спрайта, которым будет заполнен терминал после очистки. В случае использования LIB/P2TERM.RWL вызов будет выглядеть так - termclear(spr). Вот пример использования COMMAND через библиотеку LIB/P2TERM.RWL: robot "RANDOMTEST" author "Shaos" +inc/rw1_std.rwi +inc/rw1p2.rwi +myspr.rwi main() { terminfo(0) dx=A;dy=B;col=C say "DX=&DX DY=&DY COL=&COL " dd = 0 while(1) { rand=R // get 16-bit random xx=@ABS(rand)%dx rand=R // get 16-bit random yy=@ABS(rand)%dy select xx yy if(dd%2) set @MYSPR else set @MYSPR2 dd = dd+1 } } +lib/p2term.rwl Спрайты берутся отсюда: ;8x8-2/16 ;myspr.spr file MYSPR DB #FF,#81,#A5,#81,#81,#9F,#81,#FF,#40 ;@ MYSPR2 DB #FF,#81,#A5,#81,#81,#9F,#81,#FF,#10 ;# Эта программа настраивается на размер экрана системы и затем случайным образом заполняет его красными и синими рожицами :) Кроме библиотеки терминала p2term.rwl есть еще несколько библиотек: p2syst.rwl - команды, связанные с системой p2chan.rwl - команды, связанные с каналами (файлы) random.rwl - генератор 16-битных псевдослучайных чисел string.rwl - библиотека строковых функций апрель 2001 - июнь 2002 Александр Шабаршин (shaos@mail.ru) http://robots.shaos.ru