Разработка с помощью NedoOS
Alone Coder
Под NedoOS можно разрабатывать на ас─
семблере, Си,Паскале и Недоланге,но в этой
статье я расскажу именно про ассемблер. На
нём примеров больше всего, и с ним будет
более понятно, как работает система.
Кроме примеров, NedoOS предоставляет ра─
зработчику большой набор функций, которые
обычно лень писать с нуля, а также среду
для автоматической сборки.
Первое знакомство
Первое знакомство с системой для разра─
ботчика выглядит так (если у вас Windows):
1. скачать всё из http://nedoos.ru/svn/
в директорию,не содержащую пробелов в пути
2. войти в эту директорию, а точнее, в
поддиректорию src/
3. запустить mkevo.bat.
При этом пересобирается вся система (на
момент написания этой статьи - 70 паке─
тов), создаётся образ SD-карты sd_nedo.vhd
(если его не было), а на него записывается
всё, что есть в release/. Образ лежит в
us/ - директории эмулятора.
Как это работает внутри:
1. mkevo.bat записывает файлы настроек
для конфигурации ZX Evo в файл src/_sdk/
syssets.asm
2. mkevo.bat запускает make.bat с пара─
метром noneedtrd (то есть не делаем trd-
образ дискеты, она нам не нужна)
3. make.bat при необходимости создаёт
директорию release/
4. make.bat собирает пакет fatfs4os (фа─
йловая система FAT)
5. make.bat собирает пакет kernel (ядро
ОС), создаётся nedoos.$c
6. make.bat в цикле собирает все пакеты
путём вызова build.bat у каждого из них, а
после сборки пакета копирует в release/
полученные *.com, *.ext, *.txt, *.new и
поддиректорию, одноимённую с директорией
пакета
7. make.bat при отсутствии параметра
noneedtrd (а он у нас присутствует) созда─
ёт и заполняет test.trd
8. mkevo.bat переименовывает nedoos.$c в
sd_boot.$c (чтобы можно было запускать из
Evo Reset Service одной кнопкой 5)
9. mkevo.bat запускает chkimg.bat с па─
раметром sd (если бы был hdd, то работали
бы с образом жёсткого диска hdd_nedo.vhd )
10. chkimg.bat при необходимости создаёт
образ (с помощью images.exe )
11. chkimg.bat записывает на образ всё,
что есть в release/ (с помощью dmimg.exe )
12. mkevo.bat запускает настроенный эму─
лятор.
Аналогично работают сборщики для оста─
льных конфигураций:
mkevsd-g.bat - ZX Evo без SD-карты в NeoGS
mkatm3.bat - ATM3 на дискете
mkatm3hd.bat - ATM3 на HDD (Nemo IDE,можно
исправить в батнике)
mkatm3sd.bat - ATM3 на SD-карте
mkpe26.bat - Pentagon 2.666LE на дискете
mkpe26sd.bat - Pentagon 2.666LE на SD card
mkatm2.bat - ATM2 на дискете
mkatm2hd.bat - ATM2 на HDD (ATM IDE)
Они все однотипные,так что каждый может
создать свой сборщик или исправить сущест─
вующий.
makeall.bat вызывает все mk*.bat. Его
надо запускать перед коммитом в репозито─
рий.
Батник сборки
Из всего этого (особенно из пункта про
build.bat ) видно, что программу пользова─
теля лучше писать по образцу имеющихся
программ. Проще всего взять директорию
emptyapp (или, если ваша программа похожа
на какую-то существующую - то директорию
той), скопировать её целиком и исправлять.
Как выглядит простейший build.bat:
1. Настройка путей:
if "%settedpath%"=="" call ../_sdk/
setpath.bat
2. Компиляция (может быть много строк, с
вызовом нужных утилит конверсии ресурсов и
т.п.):
sjasmplus --nologo --msg=war emptyapp.asm
Вот это место вы можете исправить для
вашей программы, но если её главный модуль
называется emptyapp.asm - не исправляйте:)
А остальное исправлять, скорее всего, и
так не придётся.
3. Если build.bat вызван отдельно (у ме─
ня это кнопка F9 в Notepad++ ), то копиро─
вать в release/ и на образ SD-карты:
if "%currentdir%"=="" (
FOR %%j IN (*.com) DO (
copy /Y %%j "../../release/bin/" > nul
"../../tools/dmimg.exe" ../../us/
sd_nedo.vhd put %%j /bin/%%j
)
if "%makeall%"=="" ....usemul.exe
)
Вариант для игр с кучей файлов в одно─
имённой поддиректории:
SET releasedir2=../../../release/
if "%currentdir%"=="" (
FOR %%j IN (*.com) DO (
"../../../tools/dmimg.exe" ../../../us/
sd_nedo.vhd put %%j /nedogame/%%j
move "*.com" "%releasedir2%nedogame">nul
IF EXIST %%~nj xcopy /Y "%%~nj"
"%releasedir2%nedogame%%~nj">nul
)
cd ../../../src/
call ..toolschkimg.bat sd
if "%makeall%"=="" ..usemul.exe
)
Может быть, кто-то сможет сделать везде
одинаково? Но пока живём так.
Есть ещё вариант батника runcurl.bat,
который после сборки пытается передать со─
бранный *.com по HTTP на уже запущенный
эмулятор, а потом его удалённо запустить.
Для этого в эмуляторе должна быть запущена
NedoOS, а в ней веб-сервер 3ws.
Главный модуль программы
Предположим,что вы пишете игру.Её исхо─
дники лежат в src/games/somegame/. Её гла─
вный модуль называется main.asm. Её com-
файл называется somegame.com, а дополните─
льные файлы генерируются в src/games/
somegame/somegame/ (при сборке попадут в
release/nedogame/somegame/somegame/ ). Как
выглядит main.asm:
DEVICE ZXSPECTRUM128
include "../../_sdk/sys_h.asm"
;там все системные константы и макросы
STACK=0x4000 ;так удобнее всего для игр
org PROGSTART ;все программы начи─
;наются с этого адреса
begin
ld sp,STACK ;иначе получится sp=0
;Наша программа работает не через stdio, а
;сама ковыряется в экране, поэтому:
OS_HIDEFROMPARENT ;разбудить роди─
;теля программы (обычно навигатор)
ld e,0+128 ;e=0: EGA, e=2:MC,
;e=3: 6912, e=6: text
;+8=noturbo ;+128=keep screen
;e=-1: disable gfx
OS_SETGFX ;включаем граф.режим и
;получаем фокус (out:e=old gfxmode)
ld de,path
OS_CHDIR ;входим в поддиректорию
;...грузим файлы...
mainloop
;...играем...
jr mainloop
ld hl,0 ;результат
QUIT ;выход
path
db "somegame",0
;...всякие процедурки, таблицы, инклюды...
end
savebin "somegame.com",begin,end-begin
LABELSLIST "../../../us/user.l" ;сохраним
;метки,смотреть их дебагере Unreal Speccy
Если код вашей программы компилируется
в несколько страниц, то используйте дирек─
тиву PAGE и несколько savebin (можно под─
смотреть в проекте br, как это делается).
Отладка
Итак, если вы написали вышеприведённый
пример main.asm и запустили build.bat (не
забудьте указать там main.asm ),то как раз
попадёте в Unreal Speccy.
По кнопке "5" вы окажетесь в NedoOS,
оттуда запустите свою игру из nedogame/
(можно это автоматизировать с помощью
autoexec.bat ).Нажмите Shift+F1, чтобы по─
пасть в отладчик. Нажмите Ctrl+L, чтобы
увидеть метки (а чтобы развидеть - ещё раз
Ctrl+L ).
Убедитесь, что текущая задача - именно
ваша игра: Tab (в окно дампа), Ctrl+G,0080
- вы должны увидеть somegame.com в дампе.
Если это не так, то вернитесь в окно
дизассемблера (Shift+Tab), Ctrl+G, 0057
(конец обработчика прерываний), а дальше
жмите F4 (выполнение до курсора), пока не
увидите somegame.com в дампе. Справа внизу
видны текущие включенные страницы во всех
четырёх четвертинках.
Шагать по программе - F7. Шагать с про─
пуском вызовов - F8. Шагать до выхода по
стеку - F11. Можно переставлять текущий
адрес исполнения - Z.
В ходе исполнения программы можно вруч─
ную исправлять регистры (переход к ним -
Shift+Tab, мышка тоже работает).Можно даже
исправлять программу.
Если другие задачи (особенно term.com )
мешают трассировать вашу программу,их мож─
но закрыть: попав в эту задачу,переставьте
адрес исполнения на 0.
Посмотреть экран - F9.
Сохранить кусок памяти - Alt+W (сохра─
няется в src/ ). Загрузить - Alt+R.
Выход из отладчика - Shift+F1. В теку─
щей версии эмулятора это почему-то приво─
дит к залипанию Shift, поэтому надо ткнуть
ещё раз Shift. При выходе из отладчика та─
кже отключается Kempston mouse, его надо
захватить кликом по окну эмулятора.
Загрузка и сохранение файлов из программы
Если вам нужно прочитать только какую-
нибудь мелочь, типа отгрузки, можно просто
набросать:
ld de,filename ;"name.ext",0
OS_OPENHANDLE ;открыли файл
or a
jr nz,ошибка ;не смогли открыть
push bc ;b=хэндл открытого файла
ld de,SAVEDATA ;адрес,куда грузить
ld hl,SAVEDATAsz ;сколько байт
OS_READHANDLE ;загрузили
pop bc ;b=хэндл открытого файла
OS_CLOSEHANDLE ;закрыли
Сохранение - аналогично, только вместо
OPENHANDLE пишем CREATEHANDLE, а вместо
READHANDLE - WRITEHANDLE. Образец можно
посмотреть в untangle.
Но обычно данные надо грузить в страни─
цы, а эти страницы надо выделить!
Изначально ОС выделяет каждой программе
4 страницы,номера которых можно узнать че─
рез OS_GETMAINPAGES (получаем dehl=pages
in 0000, 4000, 8000, c000 ). Лучше всего
узнать их в самом начале и запомнить, на─
пример, так:
OS_GETMAINPAGES ;dehl=pages in
;0000,4000,8000,c000
ld a,e
ld (pgmain4000),a
ld a,h
ld (pgmain8000),a
ld a,l
ld (pgmainc000),a
;а дальше уже будет загрузка
А в конце программы написать удобные
процедуры включения этих страниц:
setpgmain4000
pgmain4000=$+1
ld a,0
SETPG4000
ret
setpgmain8000
pgmain8000=$+1
ld a,0
SETPG8000
ret
setpgmainc000
pgmainc000=$+1
ld a,0
SETPGC000
ret
Если ваша программа всегда включает
экран в 4000 и 8000, то имеет смысл объ─
единить setpgmain4000 и setpgmain8000 в
одну процедуру setpgsmain40008000. А теку─
щий экран включать через процедуру
setpgsscr40008000. Можно не писать это с
нуля, а заимствовать целиком модуль src/
games/sprexamp/mem.asm.
Итак, нам надо загрузить сколько-то фа─
йлов в страницы.Возьмём универсальную про─
цедуру загрузки одной страницы из того же
sprexamp:
loadpage
;заказывает страничку и грузит туда файл
;(имя файла в hl)
;out: hl=после имени файла, a=pg
push hl
OS_NEWPAGE
pop hl
ld a,e
push af ;pg
SETPGC000
push hl
ex de,hl
OS_OPENHANDLE
push bc
ld de,0xc000 ;addr
ld hl,0x4000 ;size
OS_READHANDLE
pop bc
OS_CLOSEHANDLE
pop hl
ld b,1
xor a
cpir ;after 0
pop af ;pg
ret
И будем её вызывать так:
ld hl,texfilename
call loadpage
ld (pg0),a
call loadpage
ld (pg1),a
call loadpage
ld (pgsfx),a
call loadpage
ld (pgmusic),a
и т.п.
В br это вообще делается в цикле, а но─
мера страниц кладутся в таблицу.
texfilename
db "pg0.bin",0
db "pg1.bin",0
db "sfx.bin",0
db "music.bin",0
Легко переделать loadpage на загрузку с
произвольного адреса, который тоже можно
взять из texfilename (допустим, перед име─
нем файла).
Рисование на экране
Есть два способа рисования - в видимом
экране или с двойной буферизацией (один
экран обновляем, другой виден).
Для динамичных игр лучше всего исполь─
зовать второй вариант, иначе возможны про─
блемы с мерцанием и исчезанием спрайтов.
Главный цикл игры, работающей на двух эк─
ранах, выглядит примерно так (пример из
loadscr ):
ld a,(timer)
ld (oldtimer),a
;главный цикл
mainloop
;вывод фона/восстановить его под спрайтами
;... рекомендую взять из sprexamp!
;вывод спрайтов
call setpgsscr40008000 ;включили
;страницы экрана
ld iy,spaceship0
ld e,50 ;e=x = -(sprmaxwid-1)..159
;(кодируется как x+(sprmaxwid-1))
ld c,50 ;c=y = -(sprmaxhgt-1)..199
;(кодируется как есть)
call prspr
call setpgsmain40008000 ;включили
;страницы программы в 4000,8000, как было
;закончили рисовать
ld a,(timer)
push af
call changescrpg ;с этого момента
;(точнее,с INT'а) увидим,что нарисовали
;логика
;вызывается столько раз,сколько прошло
;прерываний!
mainloop_uvwaittimer0
ld a,(timer)
oldtimer=$+1
ld b,0
ld (oldtimer),a
sub b
ld b,a
jr z,mainloop_uvwaittimer0 ;если
;прерываний ещё не было,крутимся тут
;b=сколько прошло прерываний
mainloop_uvlogic0
push bc
call logic ;свою логику пишем сюда
pop bc
djnz mainloop_uvlogic0
;ждём физического переключения экрана!
;можем начать новую отрисовку, только если
;с момента changescrpg прошло хотя бы одно
;прерывание (возможно, внутри logic)
pop bc ;b=timer во время changescrpg
waitchangescr0
ld a,(timer)
cp b
jr z,waitchangescr0
ld a,(curkey)
cp key_esc
jp nz,mainloop ;выход по esc/break
Чтобы работал timer, подключите модуль
sprexamp/int.asm и используйте swapimer
(включение / выключение пользовательского
обработчика прерываний) перед и после ва─
шего главного цикла. Обратите внимание,
музыка не играется в этом обработчике, для
этого есть системный вызов OS_SETMUSIC,
который гарантирует ровное проигрывание,
даже если ваша задача вытеснена другими
задачами!
Процедуру prspr возьмите из sprexamp/
prspr.asm. Если у вас спрайты скомпилиро─
ваны не в страницы, а в отдельные файлы,
то можно в них определить prsprqwid=0x100,
а перед первым вызовом prspr записать в
0x100 команду jp prsprqwid. Вообще можно
начинать написание игры с исправления про─
екта sprexamp. Там есть даже два готовых
движка - со скроллом фона по двум осям
(без скролла фон не перепечатывается) и со
скроллом по вертикали (фон перепечатыва─
ется всегда). Причём к первому движку уже
прикручено управление и логика в стиле
платформера.
Стоит прочитать nedoos.txt,api_base.txt
и sys_h.asm, чтобы получить представление
о других функциях системы.
Other articles: